L: Trạng thái ban đầu của hệ thống
3.1.3 Định danh, Hằng số và Biểu thức
•Định danh
Định danh có thể là một chữ cái, một ký tự. •Hằng số
Hằng số là một chuỗi ký tự đại diện cho một số nguyên thập phân. Hằng số tượng trưng có thể được định nghĩa như sau:
#define MAX 999 •Biểu thức
Một biểu thức được xây dựng từ các biến, hằng số và sử dụng các toán tử sau đây: +, -, *, /, %, --, ++, >, >=, <, <=, ==, !=, &&, ||, !, &, |, ~, ^, >>, <<, !, ?, (), [], 3.1.4 Tiến trình
•Một tiến trình được khai báo bắt đầu bằng một từ khóa proctype và gồm có: Tên
Danh sách thông số chính Khai báo biến cục bộ Thân chương trình
• Cú pháp của một khai báo tiến trình proctype name( /* formal parameter list */ ) {
/* các khai báo địa phương và các lệnh */
… }
/* và */ quy định giới hạn chú thích trong promela
• Tiến trình init
Tất cả các chương trình promela đều cần một tiến trình init nó giống như hàm main() trong ngôn ngữ C. Việc thực thi một chương trình promela được bắt đầu từ tiến trình init.
Một tiến trình init có dạng:
init { /* Các khai báo địa phương và các biểu thức. */ }
Đơn giản nhất có thể là chương trình promela có dạng: init { skip }
Skip có nghĩa là không có biểu thức nào trong tiến trình init;
Mỗi tiến trình được định nghĩa bởi một proctype, nó được thực thi đồng thời với tất cả các tiến trình khác, nó giao tiếp với các tiến trình khác thông qua việc sử dụng các biến công cộng hoặc các kênh. Tiến trình thực thi sau khi thực hiện hàm run.
3.2 Công cụ Spin3.2.1 Sơ lược về Spin 3.2.1 Sơ lược về Spin
Spin là công cụ mã nguồn mở, phổ biến và được sử dụng bởi hàng ngàn người trên toàn cầu. Nó được sử dụng cho việc xác minh thẩm định của các hệ thống phân phối phần mềm.
Từ năm 1980 Spin được phát triển tại Bell Labs của nhóm Unix thuộc trung tâm nghiên cứu Khoa học máy tính. Phần mềm này phát triển rộng rãi từ năm 1991.
Tháng 4 năm 2002 Spin được trao giải thưởng phần mềm uy tín cho năm 2001 của ACM.
Spin cung cấp một giả lập cho phép các nhà thiết kế đạt được kết quả phản hồi từ hệ thống mô hình của họ. Những kết quả phản hồi đó đóng vai trò quan trọng trong sự hiểu biết của các nhà thiết kế về hệ thống trước khi họ đầu tư vào phân tích chính thức.
3.2.2 Công cụ XSpin
Có giao diện thân thiện người dùng cửa sổ soạn thảo chính với khả năng chỉnh sửa và tìm kiếm là đơn giản. XSpin là công cụ thuận tiện để tiếp cận với Spin.
Hình 3: Màn hình cửa sổ chính của XSpin. • Run Syntax Check
Kiểm tra cú pháp ngôn ngữ Promela.
Hình 4: Cửa sổ chức năng Run Slicing algorithm. • Run Slicing Algorithm
Xác định những thành phần không liên quan của mô hình.
Biểu diễn sự phân tích lưu lượng dữ liệu.
• Set Simulation Parameters (Hỗ trợ gỡ lỗi quan trọng nhất) Thiết lập thông số hiển thị:
Message Sequence Chart (MSC) Panel: Cung cấp quá trình giao tiếp theo thời gian. Kiểm soát cách trình bay các liên kết giữa MSC và Promela bên trong của sổ văn bản chính được hỗ trợ thông qua bảng điều khiển này.
Time Sequence Panel: Cung cấp quá trình giao tiếp theo thời gian.
Data Values Panel: Trình bay dữ liệu theo thời gian. Tùy chọn gồm các kênh đệm các biến địa phương và biến toàn cục.
Random : Yêu cầu người sử dụng cung cấp một giá trị seed.
Hình 5: Cửa sổ chính chức năng Set Simulation Parameters. • Run Simulation
Việc thiết lập các thông số hiển thị phải được thực hiện ít nhất một lần trước khi thực hiện chạy Simulation.
Việc thiết lập mặc định thông số hiển thị sẽ sinh ra 3 của sổ sau:
Simulation Output: Cung cấp 2 phương thức : Chạy từng bước (Single Step) hoặc chạy liên tục (Run).
Data Values.
Hình 6: Cửa sổ khi chạy chức năng Run Simulation. • Thiết lập thông số Verification
Kiểm tra mô hình.
Đảm bảo thực hiện an toàn và xác minh tính chất.
Correctness Properties: Safety, Liveness.
Search mode.
A full queue.
Verify an LTL property.
Hình 7: Cửa sổ chính chức năng Set Verification Parameters. • Run Verification
Set verification parameters.
Hình 8: Cửa sổ khi chạy chức năng Run Verification. • LTL Temporal Logic Formulae
LTL = Mệnh đề Logic + toán tử điều khiển thời gian. Giúp chỉnh sửa và bảo trì các công thức logic.
Theo thời gian trong Spin
Bước 1: Chạy “LTL Property Manager”
Bước 2: Nhập vào đặc tính thời gian mà bạn muốn thẩm định. Chú ý phải là biểu thức bất biến và tên bằng chữ thường.
Bước 3: Chỉ ra là có hay không đặc tính thời gian cần giữ: all executions (desired behaviour) hoặc
no executions (error behaviour)
Bước 5: Ấn vào nút “Run Verification” và tiếp tục ấn vào nút “Run” trong của sổ “LTL Verification”
Chú ý là những thông số của LTL có thể lưu lại để sử dụng trong tương lai bằng nút “Save As” và “Load”
Hình 9: Của sổ khi chạy chức năng LTL Property Manager.
CHƯƠNG 4
SINH CA KIỂM THỬ TỰ ĐỘNG VÀ THỰC NGHIỆM
Sau khi thiết kế mô hình hệ thống bằng ngôn ngữ mô hình Promela, kết hợp nhúng mã C vào đặc tả promela và sử dụng công cụ hỗ trợ kiểm chứng Spin để tự động sinh các ca kiểm thử. Kitchen Timer là một ví dụ nhỏ được tiến hành để đánh giá các phương pháp đã đề xuất.
4.1 Phương pháp sinh các ca kiểm thử tự động
Từ mô tả các yêu cầu và chức năng xác định của hệ thống cùng với dữ liệu đầu vào, ra ta thiết kế mô hình của hệ thống. Mô hình đó biểu diễn không gian các trạng thái có thể đạt được của hệ thống. Nhúng một đoạn mã nguồn C vào đặc tả promela để khi dịch chuyển giữa các trạng thái thì ta hiển thị ra các trạng thái đó một cách tự động.
Ta có thể sử dụng một ngăn xếp để lưu các trạng thái đó lại. Cấu trúc của ngăn xếp có thể định nghĩa như sau:
typedef struct Node{ char state[256]; char input[256]; char output[256]; }Node; #define MAXSIZE 1000000 Node State_Stack[MAXSIZE];
Tiếp theo tới mỗi trạng thái ta lưu trạng thái đó và ngăn xếp: char tempLable[256] ="FIRST";
char tempInput[256] = ""; char tempOutput[256] = "";
Node tempNode = {"FIRST","",""};
strcpy(tempNode.label,cur_label); strcpy(tempNode.input, cur_input); strcpy(tempNode.output, cur_output); } In ra trạng thái đó: void printNode(Node n) { printf("Input: %s\n",n.input); printf("Output: %s\n",n.output); printf("STATE<%s>\n",n.label); } 4.2 Ví dụ áp dụng
Áp dụng các phương pháp đã đề xuất ở được tìm hiểu trong khóa luận cho bài toán Kitchen Timer. Kitchen timer một thiết bị hẹn giờ đơn dùng trong các nhà bếp. Đầu vào của thiết bị thông qua 2 nút bấm SW1 và SW2. Đầu ra là thông qua 2 đèn báo hiệu LED.
Hình 11: Kiến trúc hệ thống kitchen timer.
Timer CPU Flash M emory RAM LED LED SW1 SW2 IO Por t
4.2.1 Mô tả bài toán
•Khởi động kitchen timer
Bật công tắc khởi động kitchen timer. Khi bạn bắt đầu chương trình cả hai đèn LED đã được chờ đợi ở chế độ tắt.
•Thiết lập thời gian
Thiết lập thời gian bằng công tắc 1. Thiết lập thời gian được hiển thị ở đèn LED ở dạng số nhị phân. Thời gian thiết lập là từ 1 tới 3 phút.
•Bắt đầu tính giờ
Bắt đầu đồng hồ đếm ngược bằng công tắc 2. Nếu đồng hồ bắt đầu đếm ngược thì đèn LED sẽ nháy.
•Tạm dừng
Nếu bấm công tắc 1 trong khi đang tính giờ, thì sẽ kết thúc việc tính giờ và cả 2 đèn LED sẽ tắt hoàn toàn.
Nếu bấm công tắc 2 thì đồng hồ đếm ngược tạm dừng và đèn LED cũng dừng. Bấm lại công tắc 2 lần nữa thì đồng hồ tiếp tục tính giờ và đèn LED tiếp tục nháy.
•Hiển thị thời gian hết và báo động
Nếu kết thúc thời gian đã thiết lập,chuông báo hiệu sẽ khởi động để thông báo cho người sử dụng, đèn LED sẽ nhấp nháy trong vòng 3 giây. Trong khi báo động nếu bấm công tắc 1 hoặc công tắc 2 chuông báo động ngừng lại, và đèn LED sẽ tắt. Ngoài ra trong khi dừng chuông báo hiệu nếu lại thiết lập thời gian bằng công tắc 1 và bấm công tắc 2 thì đồng hồ đếm ngược lại bắt đầu.
4.2.2 Máy hữu hạn trạng thái của Kitchen Timer
Từ những thông số kỹ thuật được mô tả ở trên chúng ta xây dựng được mô hình máy trạng thái của Kitchen Timer như hình vẽ sau.
Hình 12: Mô hình máy hữu hạn trạng thái kitchen timer. Bộ Yếu tố Ý nghĩa Nhãn NUMINPUT COUNTDOWN PAUSE ALARM ALARMOFF
Thiết lập thời gian Đếm ngược Tạm dừng Báo giờ Ngừng báo giờ Biến in_t t alarm_t bTimeClk
Biến đại diện cho thời gian thiết lập Biến đại diện cho thời gian đếm ngược Biến đại diện cho thời gian báo chuông Biến đại diện cho hoạt động chu kỳ
Đặt biểu tượng đầu vào xSTART xTIME_SET TIMER_CLK
Thông báo hoạt động từ SW2 Thông báo hoạt động từ SW1
Thông báo hoạt động dựa vào chu kỳ Đặt biểu
tượng đầu ra
Thời gian thiết lập Đặt lại thời gian thiết lập Đặt lại thời gian đếm ngược Đặt lại thời gian báo thức Đặt thời gian đếm ngược Stop clock Start clock Giá trị in_t có thể là 0, 1, 2, 3 in_t = 0 t = 0 alarm_t = 0 t = in_t * 6 bTimeClk = 0 bTimeClk = 1 4.2.3 Đặc tả kitchen timer bằng promela có nhúng mã C
Mô tả Kitchen Timer bằng Promela gồm hai tiến trình song song gồm : Bộ hẹn giờ (Timer) và các hành động của môi trường (Trigger). Tiến trình Timer mô tả sự tính toán thời gian. Tiến trình Trigger xử lý và giám sát để mô tả nhiệm vụ trọng tâm. Một đoạn mã mô tả tiến trình Timer() (tham khảo Phụ lục A).
/* Tien trinh Timer() */
proctype Timer() { mtype evt;
/*---*/
/* Thiet lap thoi gian */
NUMINPUT_ST:
printf("TIMER: ST NUMINPUT\n"); NUMINPUT:
strcpy(tempLabel,"NUMINPUT"); setNode(tempLabel,now.in_time,now.time,now.alm_time,now.bTimeClk,temp Input,tempOutput); printNode(tempNode); }; GetEvtTimer(evt); if ::evt==TIMER_CLK -> atomic{ MUSI; c_code{ strcpy(tempInput,"TIME_CLK"); strcpy(tempOutput, ""); } } ::evt==xTIME_SET -> IncInTime(); atomic { printf("NumDsp.\n"); c_code{ strcpy(tempInput,"xTIME_SET"); strcpy(tempOutput,"Increase InTime");
} ::evt==xSTART -> if ::in_time==0 -> atomic{ MUSI; c_code{ strcpy(tempInput,"xSTART"); strcpy(tempOutput, ""); } } ::else -> SetTime(); SetTimerClk(ON); printf("STA_CNTDOWN.\n"); atomic{ c_code{ strcpy(tempInput,"xSTART");
strcpy(tempOutput, "Set Time = InTime * 6; Start clock");
};
goto COUNTDOWN_ST; }
fi;
goto NUMINPUT; ….
Đoạn mã mô tả tiến trình Environment.
/* Tien trinh Environment */ proctype Environment(){ do ::true -> SetEvtTimer(xTIME_SET); ::true -> SetEvtTimer(xSTART); ::true -> if ::bTimeClk==1 -> SetEvtTimer(TIMER_CLK); ::bTimeClk==0 -> ; fi od }
•C_decl sẽ định nghĩa một kiểu dữ liệu mới tên Node. Để tránh xung đột trùng tên, thì tên kiểu dữ liệu mới này không được trùng với bất kỳ tên dữ liệu nào đã tồn tại trong Spin. Trình biên dịch C sẽ thông báo nếu điều này vô tình xảy ra. Spin không tự phát hiện ra những xung đột này.
Bởi vì kiểu dữ liệu mới này được tham chiếu tới các khai báo khác, vì vậy chúng ta phải chắc chắn rằng định nghĩa của nó được đặt đầu đoạn mã mô hình Promela. Đoạn mã c_decl chỉ để định nghĩa kiểu dữ liệu mới C trong Promela.
c_decl{
typedef struct Node{
char label[256]; /* Nhan cua trang thai tiep theo */ int in_time; /*Thoi gian thiet lap*/
int time; /*Thoi gian dem nguoc*/ int alm_time; /*Thoi gian bao dong*/ unsigned bTimeClk; /*Chu ky dieu khien*/
char input[256]; /* bieu tuong dau vao */ char output[256]; /* bieu tuong dau ra */ }Node;
}…
Đoạn mã sau có nhiệm vụ thiết lập các thông số cho dữ liệu kiểu Node và in ra input, output và các trạng thái cùng các thông số của nó.
Đoạn mã lưu các trạng thái vào Node và in ra Node. c_code{
char tempLabel[256] = "NUMINPUT"; char tempInput[256] = "";
char tempOutput[256] = "";
Node tempNode = {"NUMINPUT",0,0,0,0,"",""};
void setNode(char* cur_label, int cur_in_time, int cur_time, int cur_alm_time, unsigned cur_bTimeClk, char* cur_input, char* cur_output) {
strcpy(tempNode.label,cur_label); tempNode.in_time = cur_in_time; tempNode.time = cur_time;
tempNode.bTimeClk = cur_bTimeClk; strcpy(tempNode.input, cur_input); strcpy(tempNode.output, cur_output); } void printNode(Node n) { printf("Input: %s\n",n.input); printf("Output: %s\n",n.output); printf("STATE<%s,%d,%d,%d, %d>\n",n.label,n.in_time,n.time,n.alm_time,n.bTimeClk); } }; 4.2.4 Kết quả
Cấu trúc của ca kiểm thử: Input:
Output:
STATE<”label”, “in_time”, time, alm_time, bTimeClk > Trong đó:
Input là biểu tượng đầu vào, Output là biểu tượng đầu ra,
Label là nhãn của trạng thái tiếp theo, In_time là thời gian thiết lập,
Time là thời gian của đồng hồ đếm ngược, Alm_time là thời gian báo chuông,
bTimeClk là trạng thái của đồng hồ (0 là OFF, 1 là ON)
Sau đây là một vài trạng thái trích trong ca kiểm thử được sinh ra từ chương trình ( tham khảo thêm tại Phụ lục B).
STATE<NUMINPUT,0,0,0,0> Input: xTIME_SET
Output: Increase InTime
STATE<NUMINPUT,1,0,0,0> Input: xTIME_SET
Output: Increase InTime
STATE<NUMINPUT,2,0,0,0> Input: xTIME_SET
Output: Increase InTime
STATE<NUMINPUT,3,0,0,0> Input: xTIME_SET
Output: Increase InTime
STATE<NUMINPUT,0,0,0,0> Input: xTIME_SET
Output: Increase InTime
STATE<NUMINPUT,0,0,0,0> Input: xSTART Output: STATE<NUMINPUT,0,0,0,0> Input: xSTART Output: STATE<NUMINPUT,0,0,0,0> Input: xSTART Output: STATE<NUMINPUT,0,0,0,0> Input: xTIME_SET
Output: Increase InTime
STATE<NUMINPUT,0,0,0,0> Input: xTIME_SET
Output: Increase InTime
STATE<NUMINPUT,3,0,0,0> Input: xSTART
Output: Set Time = InTime * 6; Start clock STATE<COUNTDOWN,3,18,0,1>
Input: xTIME_SET
Output: Clear InTime; Clear Time; Stop clock STATE<NUMINPUT,0,0,0,0>
CHƯƠNG 5 KẾT LUẬN
Trong khóa luận này tôi đã tìm hiểu lý thuyết cơ bản nhất về kiểm thử mô hình, bên cạnh đó tìm hiểu ngôn ngữ mô hình Promela từ đó áp dụng cho việc xây dựng một mô hình của hệ thống. Sau khi xây dựng được mô hình của hệ thống, kết hợp nhúng mã C vào mô tả promela và sử dụng công cụ hỗ trợ kiểm thử Spin để tự động sinh ra các ca kiểm thử.
Luận văn đã áp dụng lý thuyết đã tìm hiểu để tự động sinh ca kiểm thử cho ví dụ kitchen timer. Kết quả thực nghiệm thu được là các ca kiểm thử, kết quả đó cho thấy ứng dụng là có ích, có thể sử dụng các ca kiểm thử đó để xác minh tính đúng đắn của phần mềm với thiết kế ban đầu của hệ thống.
Tuy nhiên vì thời gian có hạn nên khóa luận mới chỉ dừng lại ở việc xây dựng mô hình từ đó tự động sinh các ca kiểm thử mà chưa kiểm tra trên cài đặt kitchen timer. Bên cạnh đó chưa kiểm tra được tính đúng đắn của việc xây dựng mô hình so với yêu cầu của bài toán.
Trong tương lai tôi sẽ tiếp tục nghiên cứu phương pháp giải quyết bài toán kiểm tra tính đúng đắn của việc xây dựng mô hình với mô tả ban đầu của hệ thống đồng thời kiểm tra trên chương trình thực tế.
Phụ lục A: Đặc tả của kitchen timer bằng promela có nhúng mã C
/* Kitchen timer */
/* Nhung ma C */
/* Cau truc du lieu cho mot Node */
c_decl{
typedef struct Node{
char label[256]; /*Nhan cua trang thai tiep
theo */
int in_time; /*Thoi gian thiet lap*/
int time; /*Thoi gian dem nguoc*/
int alm_time; /*Thoi gian bao dong*/
unsigned bTimeClk;/*Chu ky dieu khien*/
char input[256]; /* bieu tuong dau vao */
char output[256]; /* bieu tuong dau ra */
}Node; }
c_code{
char tempLabel[256] = "NUMINPUT"; char tempInput[256] = "";
char tempOutput[256] = "";
Node tempNode = {"NUMINPUT",0,0,0,0,"",""};
/* Thiet lap gia tri cho cac Node */
void setNode(char* cur_label, int cur_in_time, int cur_time, int cur_alm_time, unsigned cur_bTimeClk, char* cur_input, char* cur_output) {
strcpy(tempNode.label,cur_label); tempNode.in_time = cur_in_time; tempNode.time = cur_time; tempNode.alm_time = cur_alm_time; tempNode.bTimeClk = cur_bTimeClk; strcpy(tempNode.input, cur_input); strcpy(tempNode.output, cur_output); } /* In ra cac Node */ void printNode(Node n) { printf("Input: %s\n",n.input); printf("Output: %s\n",n.output); printf("STATE<%s,%d,%d,%d, %d>\n",n.label,n.in_time,n.time,n.alm_time,n.bTimeClk);
} };
/*---*/