Chƣơng 1 : GIỚI THIỆU
3.3 Kỹ thuật sinh kịch bản kiểm thử
3.3.1. Kịch bản kiểm thử cho các toán từ thông thƣờng
Đồ thị dòng điều khiển đƣợc sinh ra từ biểu đồ tuần tự đƣa ta trở về bài toán quen thuộc trong kiểm thử hộp trắng. Có nhiều phƣơng pháp sinh luồng kiểm thử có thể đƣợc áp dụng nhƣ: thuật toán tìm theo chiều sâu, thuật toán tìm theo chiều rộng, v.v.
Xét về mặt giải thuật, cả hai thuật toán duyệt theo chiều rộng và thuật toán duyệt theo chiều sâu đều có thể đƣợc sử dụng. Tuy nhiên, với mong muốn sinh lần lƣợt các ca kiểm thử tƣơng ứng, tức duyệt lần lƣợt từng đƣờng đi từ điểm bắt đầu tới điểm kết thúc thì thuật toán duyệt theo chiều sâu là phù hợp hơn. Vì vậy, trong bài nghiên cứu này tôi sử dụng giải thuật thuật toán duyệt theo chiều sâu để duyệt đồ thị CFG và sinh ca kiểm thử.
Thuật toán duyệt theo chiều sâu là thuật toán mà quá trình tìm kiếm đƣợc phát triển từ đỉnh con đầu tiên của nút đang tìm kiếm cho tới khi gặp đƣợc đỉnh cần tìm hoặc tới một nút không có con. Khi đó giải thuật quay lui về đỉnh vừa mới tìm kiếm ở bƣớc trƣớc và tiếp tục tìm kiếm cho tới khi duyệt hết tất cả các đỉnh trong đồ thị.
Ví dụ áp dụng thuật toán duyệt theo chiều sâu cho đồ thị Hình 3.16. Tìm kiếm ƣu tiên chiều sâu bắt đầu thăm đỉnh A, đi theo cạnh trái, tiếp tục tìm kiếm xong ở cây con trái mới chuyển sang tìm kiếm ở cây con phải. Thứ tự thăm viếng các đỉnh là: A, B, D, F, E, C, G. Quá trình viếng thăm các đỉnh diễn ra nhƣ sau: Sau khi thăm đỉnh A, vì chƣa đƣợc thăm nên theo cạnh A ta thăm , tiếp tục theo cạnh BD tới viếng thăm D. Từ D không thể tiếp tục đi xa hơn, ta quay lại B. Từ , theo F đến thăm F, từ F đến thăm E. Từ E vì A đã viếng thăm nên ta quay lại F, rồi quay lại B. Tại B vì tất cả các khả năng từ đã xem xét nên ta quay lại A. Từ A, quá trình tiếp tục với các đỉnh C và G.
A
B C E
B C E
Hình 3.16 Ví dụ cây đồ thị cần duyệt.
Áp dụng thực tế cho bài nghiên cứu, ví dụ cho đồ thị dòng điều khiển nhƣ Hình 3.17. Dựa vào mối liên hệ giữa các nốt trong đồ thị dòng điều khiển dùng thuật toán biến đổi đƣa dữ liệu về dạng chuẩn nhƣ miêu tả ở Bảng 3.2.
Qui trình:
Nốt Start đƣợc đánh số 0 nối với nốt 1 thu đƣợc cặp dữ liệu 0-1.
Nốt 1 đƣợc nối với nốt 2 thu đƣợc cặp dữ liệu 1-2.
Nốt 2 nốt tới nốt 3 và nốt 4 thu đƣợc dòng dữ liệu 2-3-4.
Nốt 3 nối tới nốt 5 thu đƣợc cặp dữ liệu 3-5.
Nốt 4 nối tới nốt 5 thu đƣợc cặp dữ liệu 4-5.
Nốt 5 nối tới nốt 6 thu đƣợc cặp dữ liệu 5-6.
Start Cook food FN Heating.start Rotate JN Checking food End 0 1 2 3 4 5 6 Hình 3.17 Đồ thị dòng điều khiển.
ảng 3.2 Dữ liệu thu thập tƣơng ứng theo các nốt đƣợc nối với nhau
Nốt bắt đầu Nốt kết thúc Dữ liệu thu thập Start 1 0 1 1 2 1 2 2 3, 4 2 3 4 3 5 3 5 4 5 4 5 5 6 5 6 6 End |
Thuật toán 3 miêu tả quá trình sinh ca kiểm thử cho các thiết kế chứa các phân đoạn thông thƣờng không có luồng song song. Đầu vào là đồ thị dòng điều khiển (ví dụ Hình 3.17) và đầu ra là chuỗi các đƣờng kiểm thử. Kết quả thu đƣợc ở Bảng 3.2 là một kết quả trung gian trong quá trình biến đổi. Các bƣớc thực hiện nhƣ sau: Từ đồ thị dòng điều khiển duyệt từng nốt và kiểm tra nốt tiếp theo thu đƣợc thông tin ma trận kề (Bảng 3.2). Kết quả ma trận kề đƣợc đƣa vào thuật toán 4 duyệt theo chiều sâu để tìm ra tất cả các đƣờng kiểm thử có thể đi từ nốt bắt đầu cho tới nốt kết thúc.
Thuật toán 3: Sinh ca kiểm thử cho các toán tử thông thƣờng.
Đầu vào: Thông tin đƣờng nối tất cả các cạnh trong đồ thị CFG.
E = {(x, y) | x, y A F} : tập tất cả các cạnh trong đồ thị CFG Trong đó:
sourceList{x}: tập các nốt nguồn - các đỉnh xuất phát x trong tập cạnh (x, y).
targetList{y} : tập các nốt đích - các đỉnh kết thúc ytrong tập cạnh (x, y).
Đầu ra: allTestPath p: là tập tất cả các đƣờng kiểm thử 1: Create new String: danhsachke;
2: danhsachke = "-1 0\n"; 3. for each element sourceList
4: danhsachke += sourceNodeId(i) + " " + targetNodeId(i);
5: if (current sourceNode == current targetNode)
6: danhsachke += " " + target.get(j).getNodeId();
7: end if
8: danhsachke += "\n"; 9: end for
10: Create new String maTranKe = danhsachke; // maTranKe = "0 1\n1 2 3\n2 1\n3 4\n4 5\n5 4 6\n6 7 8\n7 9 10\n9 -1\n10 -1\n8 11\n11 8 12\n12 -1"
11: allTestPath p = new getAllPaths(maTranKe); // thuật toán duyệt theo chiều sâu sinh ca kiểm thử
Thuật toán 4: Duyệt theo chiều sâu để sinh đƣờng kiểm thử (traverse).
Đầu vào: String maTranKe; với maTranKe là chuỗi các nốt khởi đầu và kết thúc của
các cạnh trong đồ thị CFG. Ví dụ cấu trúc một ma trận kề: maTranKe = "0 1\n1 2 3\n2 1\n3 4\n4 5\n5 4 6\n6 7 8\n7 9 10\n9 -1\n10 -1\n8 11\n11 8 12\n12 -1"
Đầu ra: String testPathString; là chuỗi tất cả các đƣờng kiểm thử
1: CreateObject Vertex (int data, int[] branchId); // tạo đối tƣợng Vertex 2: Create new ArrayList<Vertex> vertexList ;
3: Create new ArrayList<Vertex> vertexPath; 4. Create new ArrayList<Integer> testPath;
5: Split maTranke line by line; 6: for each line of maTranke
7: vertexList.add(id, branchId)); // thêm tất cả các nốt nguồn và nhánh vào vertexList là giá trị trung gian dùng để duyệt.
8: traverse(firstNode, new ArrayList<Vertex>() vertexPath) // duyệt ma trận kề bắt đầu tại nốt đầu tiên
9: vertexv = vertexList.get(0);
10: if (v == null or v.getid() == -1) // kết thúc
11. testPath.add( vertexPath.clone()); // lƣu đƣờng đi thành đƣờng kiểm thử 12: elseif (check(vertexPath, v.getId())) // duyệt tiếp nếu chƣa kết thúc 13: vertexPath.add(v);
14: inttemp = v.getBranchLengh(); 15: for (int i = 1; i < temp; i++)
16: Vertex u = getVertex(v.getBranchId(i));
17: traverse(u, vertexPath); // duyệt đệ qui 18: vertexPath.remove(vertexPath.size() - 1);
19: end for
20: end if
21: end for
Thuật toán 4 có đầu vào là ma trận kề, đầu ra là các ca kiểm thử. Các bƣớc thực hiện nhƣ sau:
Khởi tạo một đối tƣợng vertex có cấu trúc Vertex (int data, int[] branchId) trong đó int data để chứa giá trị đang duyệt, int[] branched là mảng chứa các đƣờng kiểm thử trong quá trình duyệt ma trận.
Khởi tạo mảng vertexList là biến đổi của ma trận sẽ duyệt.
Khởi tạo mảng vertexPath chứa đƣờng kiểm thử đang đƣợc duyệt.
Bắt đầu duyệt từng phần tử trong vertexList (bắt đầu từ nốt bắt đầu), nếu là nốt
không có rẽ nhánh lần lƣợt thêm vào vertexPath.
Nếu gặp nốt rẽ nhánh (là nốt đi tới nhiều hơn 1 nốt tiếp theo), tiến hành duyệt đệ
qui theo từng nhánh cho tới khi gặp nốt kết thúc thu đƣợc một đƣờng kiểm thử. Sau khi hoàn thành nhánh thứ nhất, quay lại đệ qui tiêp quá trình duyệt với các
nhánh tiếp theo thu đƣợc các đƣờng kiểm thử còn lại.
Mỗi đƣờng kiểm thử hoàn thành đƣợc thêm vào mảng testPath để có thể khôi phục
mảng vertexPath về vị trí ban đầu hoặc vị trí bắt đầu đệ qui trƣớc đó.