Đối với chương trình có vòng lặp, số lộ trình thường là vô hạn. Tuy nhiên, tồn tại tập con nhỏ nhất các lộ trình cho phép tạo ra tất cả các lộ trình còn lại. Tập con nhỏ nhất các lộ trình đó được gọi là tập các lộ trình độc lập nếu lộ trình này chứa cung mà lộ trình kia không chứa và ngược lại.
Chẳng hạn, quay trở lại đồ thị trong Hình 4.13, xét hai lộ trình:
𝑙𝑡1 = [𝑐1, 𝑐2, 𝑐3, 𝑐4]
𝑙𝑡2 = [𝑐1, 𝑐4]
Các lộ trình 𝑙𝑡1 và 𝑙𝑡2 là độc lập, vì 𝑙𝑡1 có chứa cung 𝑐2và cung 𝑐3 trong khi 𝑙𝑡2
không chứa các cung này.
Hơn nữa, McCabe [5] chỉ ra rằng số lộ trình độc lập của một đồ thị luồng điều khiển G, được ký hiệu v(G), bằng số điều kiện cộng 1. Nghĩa là đối với đồ thị trong hình 4.13, chỉ có một quyết định, vì vậy số lộtrình độc lập v(G) bằng 1 + 1 = 2. Trong ví dụ này, 𝑙𝑡1 và 𝑙𝑡2 là hai lộ trình độc lập và như vậy mọi lộ trình còn lại đều có thể biểu diễn (bằng kết hợp tuyến tính) từ hai lộ trình này.
Chẳng hạn, xét lộtrình khác như sau:
𝑙𝑡3 = [𝑐1, 𝑐2, 𝑐3, 𝑐2, 𝑐3, 𝑐2, 𝑐3, 𝑐4]
Khi đó, chúng ta có thể biểu diễn lộ trình 𝑙𝑡3như sau:
𝑙𝑡3 = 3(𝑙𝑡1 – 𝑙𝑡2 ) + 𝑙𝑡2 = 3 𝑙𝑡1 – 𝑙𝑡2
Tiêu chí bao phủ lộ trình độc lập yêu cầu bao phủ đúng v(G) lộ trình độc lập. Nếu chúng ta bao phủ số lộ trình nhỏ hơn v(G), thì luôn tồn tại một lộ trình độc lập chưa được kiểm thử. Ngược lại, nếu chúng ta bao phủ số lộ trình nhiều hơn v(G), thì sự kiểm thử sẽ dư thừa về mặt lộ trình độc lập. Lưu ý rằng, thông thường có nhiều lộ trình độc lập khác nhau đối với một chương trình. Chẳng hạn, chúng ta có thể chọn
tập lộtrình độc lập { 𝑙𝑡1, 𝑙𝑡2}.
Ngoài ra, bất kỳ một cung nào trong luồng đồ thị điều khiển đều thuộc ít nhất một lộ trình độc lập. Điều đó có nghĩa là, tiêu chí bao phủ lộ trình độc lập thỏa mãn kéo theo tiêu chí bao phủ cung thỏa mãn.
Các bước xác định dữ liệu thử thỏa mãn tiêu chí bao phủ lộ trình độc lập như sau:
- Bước 1: Xác định số lộ trình độc lập v(G) bằng cách đếm số quyết định trong chương trình.
- Bước 2: Chọn một dữ liệu thử bất kỳ. Thông thường, chúng ta chọn dữ liệu thử nhằm thực thi lộ trình bao phủ số lượng đỉnh quyết định lớn nhất trong đồ thị.
- Bước 3: Chọn dữ liệu thửthay đổi giá trị lô-gic của đỉnh quyết định đầu tiên trong lộ trình kết quả của Bước 2, tức là chọn cung khác của đỉnh quyết định đó. Lặp lại bước 3 cho đến khi tất cảcác đỉnh quyết định được thay đổi giá trị.
Chúng ta xem xét chương trình cụ thể sau: int abc ( ) { char kytu; int ketqua = 0; kytu = getchar ( ); if ( kytu = = ‘a’ ) { kytu = getchar ( );
while ( ( kytu = = ‘b’ ) // ( kytu = = ‘c’ ) ) kytu = getchar ( );
if ( kytu = = ‘x’ ) ketqua = 1; }
return ketqua; }
Hàm abc nhằm nhận biết các chuỗi ký tự (được nhập vào từng ký tự) bắt đầu bởi ký tự a theo sau các ký tự b hoặc c và kết thúc bởi ký tự x. Nếu chuỗi ký tựđúng, hàm trả về giá trị 1, ngược lại trả về giá trị 0. Đồ thị luồng điều khiển biểu diễn hàm abc được trình bày trong Hình 4.14.
- Bước 1: Chương trình có hai lệnh if và một lệnh while với hai điều kiện tương ứng với các đỉnh (𝑛1, 𝑛3, 𝑛5,và 𝑛6), vậy v(G) = 4 + 1 = 5. Chúng ta cần bao phủnăm lộ trình độc lập.
- Bước 2: Chọn dữ liệu thử DT1 = {abcx}, nghĩa là nhập vào ký tự a, tiếp theo ký tự b, tiếp theo ký tự c và cuối cùng là ký tự x. Lộtrình được thực thi là:
𝑙𝑡1 = [𝑛0, 𝑛1, 𝑛2, 𝑛3, 𝑛4, 𝑛3, 𝑛5, 𝑛4, 𝑛3, 𝑛5, 𝑛6, 𝑛7, 𝑛8]
- Bước 3:
+ Chọn quyết định đầu tiên lộ trình 𝑙𝑡1, đó là đỉnh 𝑛1. Tại đỉnh này, lộ trình 𝑙𝑡1
đã bao phủ cung (𝑛1, 𝑛2), vậy chúng ta thay đổi và chọn cung (𝑛1, 𝑛8). Để thực hiện điều đó, chọn dữ liệu DT2 = {x}, khi đó lộ trình được thực thi tương ứng là:
𝑙𝑡2 = [𝑛0, 𝑛1, 𝑛8]
Hình 3.22. Đồ thị luồng điều khiển biểu diễn hàm abc
Lộ trình 𝑙𝑡2 chứa cung (𝑛1, 𝑛8), trong khi lộ trình 𝑙𝑡1 không chứa cung này. Vậy, hai lộ trình 𝑙𝑡1 và 𝑙𝑡2 là độc lập.
+ Tiếp tục trên lộ trình 𝑙𝑡1, chọn đỉnh quyết định thứ hai, đó là đỉnh 𝑛3. Tại đỉnh này, lộ trình bao phủ cung (𝑛3, 𝑛4), vậy chúng ta thay đổi và chọn cung (𝑛3, 𝑛5).
Để thực hiện điều này, chúng ta chọn dữ liệu DT3 = {abcx}. Lộtrình tương ứng được thực thi là:
Lộ trình 𝑙𝑡1 chứa cung (𝑛3, 𝑛4), lộ trình 𝑙𝑡3 không chứa cung này. Lộ trình 𝑙𝑡2
chứa cung (𝑛1, 𝑛8), lộ trình 𝑙𝑡3 cũng không chứa cung này. Vậy các lộ trình 𝑙𝑡1, 𝑙𝑡2 và
𝑙𝑡3 độc lập từng đôi một.
+ Tiếp tục chọn đỉnh quyết định thứ ba trên lộ trình 𝑙𝑡1 (lưu ý rằng nếu lộ trình
𝑙𝑡1 không còn đỉnh quyết định, thì chọn các đỉnh quyết định trên lộ trình 𝑙𝑡2 hoặc 𝑙𝑡3), đó là đỉnh 𝑛5. Tại đỉnh này, lộ trình 𝑙𝑡1 đã bao phủ cung (𝑛4, 𝑛5), vậy chúng ta chọn cung còn lại (𝑛5, 𝑛6). Để thực hiện điều đó, chúng ta chọn dữ liệu thử DT4 = {ax}. Lộ trình tương ứng được thực thi là:
𝑙𝑡4 = [𝑛0, 𝑛1, 𝑛2, 𝑛3, 𝑛5, 𝑛6, 𝑛7, 𝑛8]
Chúng ta có thể dễ dàng nhận thấy rằng, các lộ trình 𝑙𝑡1, 𝑙𝑡2, 𝑙𝑡3 và 𝑙𝑡4 độc lập. Thật vậy, lộ trình 𝑙𝑡1 chứa cung (𝑛3, 𝑛4), lộ trình 𝑙𝑡4 không chứa cung này. Lộ trình 𝑙𝑡2 chứa cung (𝑛1, 𝑛8), lộ trình 𝑙𝑡4 không chứa cung này. Lộ trình 𝑙𝑡3 chứa cung (𝑛3, 𝑛4), lộ trình 𝑙𝑡4 cũng chứa cung này.
+ Chọn đỉnh quyết định cuối cùng trên lộ trình 𝑙𝑡1, đó là đỉnh 𝑛6. Tại đỉnh này, lộ trình 𝑙𝑡1 bao phủ cung (𝑛6, 𝑛7), vậy chúng ta chọn cung còn lại (𝑛6, 𝑛8). Để thực hiện điều đó, chúng ta chọn dữ liệu thử DT5 = {abc}. Lộ trình tương ứng được thực thi là:
𝑙𝑡5 = [𝑛0, 𝑛1, 𝑛2, 𝑛3, 𝑛4, 𝑛3, 𝑛5, 𝑛4, 𝑛3, 𝑛5, 𝑛6, 𝑛8]
Lộ trình 𝑙𝑡5 chứa cung (𝑛6, 𝑛8), trong khi các lộ trình 𝑙𝑡1, 𝑙𝑡2, 𝑙𝑡3 và 𝑙𝑡4 không
chức cung này. Vậy 5 lộtrình này độc lập.
Chúng ta xác định đầy đủ 5 lộ trình độc lập và 5 dữ liệu thử tương ứng thỏa mãn tiêu chí bao phủ lộtrình độc lập: DT1 = {abcx} DT2 = {x} DT3 = {abcx} DT4 = {ax} DT5 = {abc}
Mặc dù, các bước xác định dữ liệu thử thỏa mãn tiêu chí bao phủ lộ trình độc lập là kỹ thuật kiểm thử hiệu quả nhằm bao phủ đồ thị. Tuy nhiên những kỹ thuật này vẫn có những hạn chế sau [4]:
- Việc thay đổi giá trị tại đỉnh quyết định (chọn cung chưa được bao phủ) không luôn tạo ra lộ trình độc lập.
- Kỹ thuật này nhằm bao phủ v(G) lộ trình độc lập. Nếu lộ trình chứa một lỗi và lộ trình này không thuộc tập các lộtrình độc lập được chọn. Việc bao phủ tất cả các lộ trình độc lập không hoàn toàn đảm bảo bao phủ thật sự mọi lộ trình có thể của đồ thị, nghĩa là lỗi chưa chắc được phát hiện.
Nhằm minh họa hai hạn chế trên, chúng ta xem xét một chương trình có lỗi nhằm tìm kiếm một phần tử e có thuộc mảng a chứa hai phần tử như sau:
main ( )
{
int a [2];
int e, i, found;
scanf (“ %d %d%d%d”, &i, &e, &a [0], &a [1] ); found = 0; while (i <= 1) { if (a[i] = = e) found = 1; else found = 0; i = i + 1; } printf (“%d”, found); }
Đồ thị luồng điều khiển tương ứng của chương trình này được trình bày trong Hình 4.15.
Số lộ trình độc lập của đồ thị trên là v(G) = 2 + 1 = 3. Chẳng hạn, chọn dữ liệu thử DT1 = [i = 3] thì lộ trình tương ứng được bao phủ là:
𝑙𝑡1 = [𝑐1, 𝑐2]
Để thay đổi cung được bao phủ tại đỉnh quyết định 𝑛1 thuộc lộ trình 𝑙𝑡1, chọn dữ liệu DT2 = {i = 0, e = 10, a[1] = 10}, khi đó lộtrình tương ứng được bao phủ là:
𝑙𝑡2 = [𝑐1, 𝑐3, 𝑐4, 𝑐6, 𝑐8, 𝑐3, 𝑐5, 𝑐8, 𝑐2]
đỉnh quyết định 𝑛2 thuộc lộ trình 𝑙𝑡2 và thay đổi cung đã được bao phủ tại đỉnh này, chúng ta chọn dữ liệu thử DT3 = {i = 0, e = 10, a[0] = 10, a[1] = 20}, khi đó lộ trình tương ứng được bao phủ là:
𝑙𝑡3 = [𝑐1, 𝑐3, 𝑐5, 𝑐7, 𝑐8, 𝑐3, 𝑐4, 𝑐6, 𝑐8, 𝑐2]
Hình 3.23. Đồ thị luồng điều khiển biểu chương trình tìm kiếm phần tử trong mảng
Tuy nhiên, chúng ta nhận thấy rằng hai lộ trình 𝑙𝑡2 và 𝑙𝑡3 chứa cùng các cung như nhau, vì vậy 𝑙𝑡2 không độc lập so với 𝑙𝑡3. Để giải quyết vấn đề này, chúng ta chọn hai lộ trình 𝑙𝑡4 và 𝑙𝑡5 với các dữ liệu thử như sau:
DT4 = {i = 0, e = 10, a[0] = 10, a[1] = 10} Bao phủ lộ trình sau: 𝑙𝑡4 = [𝑐1, 𝑐3, 𝑐5, 𝑐7, 𝑐8, 𝑐3, 𝑐5, 𝑐7, 𝑐8, 𝑐2] Và DT5 = {i = 0, e = 10, a[0] = 30, a[1] = 30} Bao phủ lộ trình sau: 𝑙𝑡5 = [𝑐1, 𝑐3, 𝑐4, 𝑐6, 𝑐8,𝑐3, 𝑐4, 𝑐6,𝑐8, 𝑐2] Chúng ta có thể dễ dàng nhận thấy rằng, lộ trình 𝑙𝑡4 và 𝑙𝑡5 là độc lập. Tuy nhiên bộ ba dữ liệu DT1, DT4 và DT5 bao phủ lộtrình độc lập không phát hiện được lỗi của chương trình. Trong khi, dữ liệu DT3 tương ứng với lộ trình 𝑙𝑡3 phát hiện được lỗi (chỉ kiểm tra phần tử cuối cùng có bằng e) không được lựa chọn do lộ trình 𝑙𝑡4
không độc lập với 𝑙𝑡2.
Mặc dù các hạn chế được minh họa trong ví dụ này, tiêu chí bao phủ lộ trình độc lập được áp dụng và chấp nhận rộng rãi hơn so với các tiêu chí bao phủ cung và
đỉnh.