Tải bản đầy đủ (.pdf) (9 trang)

Tổng quan về phương pháp sinh dữ liệu kiểm thử tự động từ mã nguồn

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (309.96 KB, 9 trang )

TỔNG QUAN VỀ PHƯƠNG PHÁP
SINH DỮ LIỆU KIỂM THỬ TỰ ĐỘNG
TỪ MÃ NGUỒN
Trần Nguyên Hương
Trường Cao đẳng Sư phạm Trung ương
Email:

Vũ Mạnh Điệp
Trường Cao đẳng Sư phạm Trung ương
Email:

Tóm tắt: Kiểm thử là quá trình kiểm tra chương trình với mục đích phát hiện lỗi. Kiểm
thử phần mềm cần nhiều thời gian và chi phí của dự án, thông thường chiếm 50% chi phí
của dự án và 35% tổng thời gian phát triển phần mềm. Bước quan trọng của kiểm thử
phần mềm là tự động sinh các bộ dữ liệu kiểm thử từ mã nguồn một cách tối ưu dựa trên
các tiêu chuẩn cho trước. Bài báo này tóm tắt các kỹ thuật chính trong việc sinh dữ liệu
kiểm thử tự động từ mã nguồn và một số hướng nghiên cứu cải tiến.
Từ khóa: Kiểm thử phần mềm, sinh dữ liệu kiểm thử tự động, kiểm thử tĩnh, kiểm thử
động, mã nguồn.

I. TỔNG QUAN VỀ SINH DỮ LIỆU KIỂM
THỬ DỰA TRÊN MÃ NGUỒN
Trong quá trình phát triển phần mềm, kiểm
thử là một giai đoạn quan trọng và thực sự
cần thiết để tạo ra phần mềm có chất lượng
cao. Có nhiều mức kiểm thử trong giai đoạn
này, bao gồm kiểm thử đơn vị, kiểm thử tích
hợp, kiểm thử hệ thống và kiểm thử chấp
nhận. Trong các mức trên thì kiểm thử đơn
vị (unit test) là một trong những pha quan
trọng nhất để đảm bảo chất lượng của phần


mềm. Hai phương pháp được sử dụng phổ
biến trong kiểm thử đơn vị gồm kiểm thử
hộp đen (black-box testing) và kiểm thử hộp
trắng (white-box testing). Kiểm thử hộp đen
chỉ kiểm tra tính đúng đắn của đầu ra với đầu
vào cho trước mà không quan tâm đến mã

nguồn chương trình. Ngược lại, phương pháp
kiểm thử hộp trắng đánh giá chất lượng mã
nguồn bằng cách sử dụng các kĩ thuật phân
tích mã nguồn. Do kiểm thử hộp trắng đi sâu
vào phân tích mã nguồn nên kĩ thuật này cho
phép phát hiện các lỗi tiềm ẩn mà kiểm thử
hộp đen không phát hiện được. Tuy nhiên, chi
phí kiểm thử hộp trắng lớn hơn rất nhiều so
với kiểm thử hộp đen. Đặc biệt, trong các dự
án công nghiệp, chi phí kiểm thử hộp trắng
có thể chiếm hơn 50% tổng chi phí phát triển
phần mềm. Nguyên nhân của tình trạng này
là do số lượng hàm cần kiểm thử lên tới hàng
nghìn, thậm chí hàng chục nghìn. Kết quả là
chi phí để kiểm thử hết những hàm này khá
lớn, ảnh hưởng khá nhiều đến tốc độ phát
triển dự án. Vì thế, quá trình kiểm thử hộp
TẠP CHÍ KHOA HỌC 3
QUẢN LÝ VÀ CÔNG NGHỆ


trắng cần được tự động hóa để giải quyết bài
toán về chi phí. Hiện nay, đa số quá trình thực

hiện tự động hóa đều tập trung vào việc thực
thi ca kiểm thử (test case) mà không quan tâm
đến việc thiết kế ca kiểm thử (việc phát hiện lỗi
phần mềm phụ thuộc chủ yếu vào chất lượng
các ca kiểm thử). Hai thành phần chính trong
thiết kế ca kiểm thử là thiết kế dữ liệu kiểm
thử và kết quả đầu ra mong đợi (expected
output). Tuy nhiên, thiết kế các kết quả đầu ra
mong đợi là khó khăn, khó tự động hóa. Do
vậy trong thiết kế ca kiểm thử người ta quan
tâm nhiều đến sinh dữ liệu kiểm thử.
Cho đến nay, có hai kĩ thuật chính để
sinh dữ liệu kiểm thử là kĩ thuật kiểm thử tĩnh
(static testing) và kiểm thử động (dynamic
testing). Điểm chung của các kĩ thuật là sử
dụng kĩ thuật thực thi tượng trưng (symbolic
execution) và sinh dữ liệu kiểm thử qua giải
hệ ràng buộc sử dụng kĩ thuật sinh ngẫu
nhiên hoặc bộ giải SMT-Solver. Kĩ thuật thực
thi tượng trưng, nêu trong do James C. King
giới thiệu lần đầu tiên vào năm 1976, sau đó
đã có một số cải tiến trong [5][6] và là một
kĩ thuật phổ biến để sinh dữ liệu kiểm thử tự
động. Trong bài toán sinh dữ liệu kiểm thử,
từ đầu vào là đường thi hành, kỹ thuật này sẽ
thay thế các giá trị đầu vào cụ thể bằng các
giá trị tượng trưng để đại diện cho một miền
các mà hành vi chương trình là như nhau.
Tư tưởng chính của kỹ thuật kiểm thử
tĩnh là sinh dữ liệu kiểm thử bằng phân tích

mã nguồn (không thực thi mã nguồn) sử
dụng kĩ thuật thực thi tượng trưng. Quy trình
thực hiện như sau: (1) mã nguồn được phân
tích và chuyển thành đồ thị dòng điều khiển
(control flow graph - CFG) theo tiêu chuẩn
bao phủ (coverage criteria) cho trước; (2) sinh
các đường kiểm thử (test path) bằng cách
duyệt đồ dòng điều khiển; (3) sinh ra hệ ràng
buộc từ đường kiểm thử; (4) sinh dữ liệu kiểm
thử (ngẫu nhiên hoặc sử dụng bộ giải SMTsolver). Các bước (2), (3), (4) được lặp lại cho
đến khi đạt tiêu chí độ phủ hoặc đã duyệt hết
4 TẠP CHÍ KHOA HỌC

QUẢN LÝ VÀ CÔNG NGHỆ

các đường kiểm thử.
Kỹ thuật kiểm thử tĩnh có ưu điểm là tốc
độ thực thi nhanh so với kỹ thuật kiểm thử
động, số dữ liệu kiểm thử sinh ra ít (đặc biệt là
trong trường hợp chương trình có vòng lặp).
Tuy nhiên có hạn chế là độ phức tạp cao vì
phải phân tích toàn bộ mã nguồn, kỹ thuật này
khó áp dụng cho các dự án công nghiệp bởi
vì hỗ trợ tất cả mọi cú pháp là điều không thể.
Trái ngược với kỹ thuật kiểm thử tĩnh, kỹ
thuật kiểm thử động không yêu cầu phải phân
tích mọi cú pháp của chương trình để sinh dữ
liệu kiểm thử. Để giảm chi phí phân tích mã
nguồn mà vẫn đạt độ phủ cao, kỹ thuật kiểm
thử động kết hợp quá trình phân tích cú pháp

chương trình với trình biên dịch [1] [2] [3] [10].
Bởi thế, kỹ thuật kiểm thử động dễ dàng đạt
được độ phủ cao mà không cần phải phân
tích chương trình nhiều.
Kỹ thuật kiểm thử động gồm hai kĩ thuật
kiểm thử được sử dụng phổ biến gồm kĩ
thuật EGT (execution generated testing) và kĩ
thuật kiểm thử tự động định hướng (concolic
testing):
Kĩ thuật EGT được áp dụng trong công cụ
sinh dữ liệu kiểm thử tự động nổi tiếng KLEE
[2] – một công cụ được đánh giá cao bởi tính
hiệu quả của nó. Tư tưởng chính của kĩ thuật
EGT là vừa chạy chương trình vừa sinh dữ
liệu kiểm thử trực tiếp. Chẳng hạn, khi gặp một
điều kiện (điểm quyết định trên đồ thị CFG),
dữ liệu kiểm thử tương ứng nhánh đúng và
nhánh sai của điều kiện này được sinh ra. Tại
đây, với mỗi dữ liệu kiểm thử, một tiến trình
mới được tạo ra sẽ phân tích chương trình
theo nhánh đúng/sai đó. Quá trình sinh dữ liệu
kiểm thử chỉ dừng khi một trong ba điều kiện
sau thỏa mãn (i) đạt độ phủ tối đa (ii) không
còn nhánh đúng/sai nào để phân tích tiếp, (iii)
đạt đến giới hạn thời gian cho phép. Nhược
điểm chính của kĩ thuật EGT là hiệu suất thấp
khi kiểm thử hàm chứa vòng lặp có số lần lặp
lớn, hoặc chứa lời gọi đệ quy. Khi đó, số tiến



trình được tạo ra có thể từ hàng trăm tới hàng
nghìn. Kĩ thuật này thể hiện tính hiệu quả khi
tìm các lỗi tiềm ẩn trong chương trình bởi vì
EGT xem xét mọi trường hợp có thể xảy ra.
Tuy nhiên, kĩ thuật EGT không phù hợp với
bài toán sinh dữ liệu kiểm thử đạt độ phủ tối
đa bởi vì chúng ta không cần xem xét hết mọi
trường hợp khi sinh dữ liệu kiểm thử.
Kĩ thuật kiểm thử tự động định hướng
được đề xuất vào năm 2005 và cài đặt trong
công cụ DART [3]. Tư tưởng của kĩ thuật
kiểm thử tự động định hướng là sinh dữ liệu
kiểm thử kế tiếp từ các dữ liệu kiểm thử trước
đó. Sau này, kĩ thuật kiểm thử tự động định
hướng liên tục được cải tiến trong các công
cụ PathCrawler [10], CUTE [4], CAUT [8][9],
và CREST [1]. Quy trình kiểm thử tự động
định hướng do Koushik Sen cùng các cộng
sự đề xuất DART [3] gồm các bước như sau:
Bước 1: Chèn các câu lệnh đánh dấu vào
hàm cần kiểm thử (instrument function). Các
câu lệnh đánh dấu giúp xác định được danh
sách câu lệnh được thực thi khi chạy chương
trình.
Bước 2: Sinh ngẫu nhiên một bộ dữ liệu
kiểm thử đầu tiên dựa trên tham số truyền vào
hàm (kiểu cơ sở, con trỏ, mảng, dẫn xuất).
Bước 3: Thực thi chương trình với dữ liệu
kiểm thử vừa tìm được. Nếu không thực thi
được (lỗi xảy ra) thì quay lại bước 2 để sinh

bộ giá trị khác.
Bước 4: Tìm tập các câu lệnh đã được đi
qua với dữ liệu kiểm thử ở bước 3 (đường thi
hành – test path) để xây dựng được hệ ràng
buộc tương ứng.
Bước 5: Tính độ phủ đạt được với dữ liệu
kiểm thử mới nhất. Nếu độ phủ đạt được tối
đa hoặc hết thời gian, quá trình sinh dữ liệu
kiểm thử kết thúc. Ngược lại sang bước 6
Bước 6: Phủ định hệ ràng buộc thu được
ở bước 4 để sinh các hệ ràng buộc mới có tác

dụng sinh các dữ liệu kiểm thử kế tiếp. Nếu
không thể sinh hệ phủ định nào khác, thuật
toán kết thúc.
Bước 7: Giải hệ ràng buộc thu được ở
bước 6 để sinh dữ liệu kiểm thử kế tiếp. Nếu
không có dữ liệu kiểm thử nào thỏa mãn, quay
về bước 6 để tìm hệ ràng buộc phủ định mới
sao cho khác hệ ràng buộc hiện tại. Ngược
lại, quay lại bước 3 để chạy dữ liệu kiểm thử
kế tiếp này.
II. NHỮNG HƯỚNG NGHIÊN CỨU HIỆN
NAY
2.1. Phân tích chương trình, tiền xử lý mã
nguồn
Trước khi thực thi chương trình để sinh dữ
liệu kiểm thử tự động từ mã nguồn, chương
trình cần phải phân tích, tiền xử lý mã nguồn.
Tuy nhiên, phân tích đầy đủ mã nguồn cho

một ngôn ngữ lập trình là điều rất khó khăn
nhất là khi ngôn ngữ lập trình thường xuyên
có sự nâng cấp thành phiên bản mới. Hiện
nay, các ngôn ngữ lập trình được phân tích
nhiều là ngôn ngữ C/C++, Java, C#. Tuy
nhiên, việc phân tích mã nguồn chủ yếu tập
trung hỗ trợ cú pháp và các kiểu dữ liệu cơ
bản, kiểu con trỏ, kiểu mảng, xử lý vòng lặp.
Kỹ thuật thường được áp dụng là sử dụng thư
viện phân tích mã nguồn, chẳng hạn Eclipse
CDT cho C/C++. Đầu vào của Eclipse CDT là
mã nguồn, đầu ra là cây cú pháp trừu tượng
(Abstract Syntax Tree – AST) ứng với mã
nguồn đó. Từ AST, người ta sẽ xây dựng đồ
thị CFG làm cơ sở cho việc thực thi các bước
tiếp theo của quá trình kiểm thử tự động.
Hiện nay đã có một số nghiên cứu quan
tâm đến giải quyết tính hướng đối tượng của
ngôn ngữ lập trình, chẳng hạn chương trình
có tính đa hình động, khuôn hình lớp.
Vấn đề phân tích mã nguồn cần tiếp tục
cải tiến để có thể hỗ trợ phân tích đầy đủ cho
các chương trình C/C++, Java… và nhiều
TẠP CHÍ KHOA HỌC 5
QUẢN LÝ VÀ CÔNG NGHỆ


ngôn ngữ khác. Các vấn đề còn đang được
nghiên cứu như vấn đề quản lý bộ nhớ, phân
tích các chương trình có tính kế thừa, chồng

toán tử, chồng hàm, khuôn hình v.v. Mặt khác,
tối ưu hóa quá trình phân tích mã nguồn là
một vấn đề mở cần được nghiên cứu.
2.2. Chiến lược tìm đường thi hành
Sau khi thực thi bộ dữ liệu kiểm thử, tập
hợp các câu lệnh được thực thi sẽ tạo thành
đường thi hành (test path). Hiện tại, nhiều
công trình nghiên cứu đưa ra nhiều chiến
lược chọn đường thi hành khác nhau để sinh
dữ liệu kiểm thử kế tiếp càng tăng độ phủ
càng tốt như [1], [8], [10]. Theo tư tưởng của
kĩ thuật kiểm thử tự động định hướng, bộ dữ
liệu kiểm thử kế tiếp được sinh ra từ nhánh/
câu lệnh chưa được đi qua bởi các bộ dữ liệu
kiểm thử trước đó. Có hai loại chiến lược tìm
đường thi hành chính bao gồm chiến lược
truyền thống và dựa trên đồ thị dòng điều
khiển (CFG-based). Chiến lược truyền thống
được được Patrice Godefroid đề xuất vào
năm 2005 và được áp dụng trong công cụ
DART. Các kĩ thuật tìm kiếm trong chiến lược
truyền thống gồm: tìm kiếm theo chiều sâu
(DFS), tìm kiếm theo chiều rộng (BFS) và tìm
kiếm ngẫu nhiên. Theo chiến lược này, điều
kiện cuối cùng của đường thi hành mới nhất
được phủ định để sinh dữ liệu kiểm thử tiếp
theo mà không xét đến trạng thái của đồ thị
luồng điều khiển. Tuy nhiên, việc phủ định này
có thể khiến quá trình sinh dữ liệu kiểm thử bị
lặp vô hạn trong trường hợp hàm có vòng lặp.

Sau này, các nghiên cứu trong PathCrawler
[10] và CUTE [4] giải quyết vấn đề này bằng
cách hạn chế số lần lặp tối đa của vòng lặp.
Tiếp nối với các nghiên cứu trước đó, số
lượng bộ dữ liệu kiểm thử được giảm thiểu hơn
nữa bởi các nghiên cứu của nhóm CREST [1],
nhóm CAUT [8] [9] do áp dụng chiến lược dựa
trên đồ thị dòng điều khiển để chọn nhánh phủ
định tốt nhất. Cụ thể là CREST sử dụng thuật
toán Dijkstra để tìm đường thi hành ngắn nhất
6 TẠP CHÍ KHOA HỌC

QUẢN LÝ VÀ CÔNG NGHỆ

từ các câu lệnh/nhánh đã thăm tới khối lệnh
chưa được thăm; CAUT cố gắng tìm đường
thi hành tốt nhất từ câu lệnh đã được thăm
đến khối lệnh chưa được khám phá.
Các tác giả Nguyễn Đức Anh và Phạm
Ngọc Hùng đã đề xuất kĩ thuật xếp hạng đường
thi hành theo độ ưu tiên trong [7]. Đường thi
hành tăng độ phủ càng lớn thì độ ưu tiên càng
cao. Mức độ tăng độ phủ được đánh giá qua
trạng thái đồ thị dòng điều khiển (CFG). Trong
trường hợp hai đường thi hành cùng tăng độ
phủ bằng nhau thì đường thi hành ngắn hơn
được ưu tiên hơn. Nguyên nhân bởi vì chi phí
phân tích mã nguồn khá lớn, những đường thi
hành ngắn hơn được ưu tiên hơn các đường
thi hành khác để giảm chi phí phân tích mã

nguồn.
Ngoài các chiến lược ở trên, hai nhóm
chiến lược sau đã được nghiên cứu và sử
dụng là nhóm chiến lược tìm kiếm heuristic
(Heuristic Search Strategy) và nhóm chiến
lược loại bỏ dư thừa (Pruning Redundance
Strategy). Đây là các nghiên cứu có nhiều kết
quả tốt và phù hợp.
2.3. Tối ưu hóa và giải hệ ràng buộc
Kích thước của hệ ràng buộc có thể khá
lớn, và cấu trúc khá phức tạp làm tăng thời
gian giải hệ ràng buộc. Điều đó dẫn đến bài
toán tối ưu hệ ràng buộc trước khi sử dụng
SMT-Solver để giải hệ ràng buộc đó.
+ Loại bỏ ràng buộc không liên quan:
Trước khi giải hệ ràng buộc cần xem xét
để loại bỏ các ràng buộc không liên quan,
nhằm tối ưu hóa hệ ràng buộc. Một vài kĩ thuật
đơn giản để giảm độ phức tạp ràng buộc như
kĩ thuật đơn giản hóa biểu thức (ví dụ: x+0 >
1 đơn giản hóa thành x >1), kĩ thuật suy biến
nhanh giá trị biến (ví dụ: x+1=10 đơn giản hóa
thành x=9), kĩ thuật loại bỏ hệ ràng buộc hiển
nhiên (ví dụ: x<10, x==5 đơn giản hóa thành
x==5), v.v.


Một chú ý quan trọng là mỗi đường thi
hành chỉ phụ thuộc vào các điều kiện với một
số lượng nhỏ các tham biến. Do vậy một cách

tối ưu hóa hiệu quả là cố gắng loại bỏ hết các
ràng buộc không liên quan đến kết quả của
đường thi hành hiện thời. Ví dụ: Xem xét ràng
buộc của một đường thi hành hiện thời của
thực thi là: (x+y>0)^(z>0)^(y<12)^(z-x=0). Giả
sử chúng ta muốn sinh các giá trị đầu vào
mới từ giải ràng buộc (x+y>0)^(z>0)& ¬(y<12)
trong đó ¬(y<12) là phủ định của nhánh khả
thi được xây dựng từ ràng buộc trên. Ta có thể
an toàn loại bỏ z vì ràng buộc này không ảnh
hưởng đến kết quả của nhánh ¬(y<12). Loại
bỏ ràng buộc thừa với tham chiếu con trỏ và
mảng đã được trình bày chi tiết trong các tài
liệu [4].
+ Kỹ thuật giải hệ ràng buộc:
Kĩ thuật tối ưu đầu tiên là kĩ thuật giải hệ ràng
buộc tăng dần (incremental solving technique)
được đề xuất trong CUTE [4], EXE [11], KLEE
[2] và CAUT [8]. Tư tưởng chính của kĩ thuật
này là chỉ những hệ ràng buộc liên quan đến
hệ ràng buộc phủ định cuối cùng được giải. Ví
dụ: Xem xét ràng buộc của một đường đi hiện
thời của thực thi là: ((x+y<10)^(x>5), một bộ
dữ liệu thỏa mãn là {x=6, y=3}. Chúng ta có
thể nhanh chóng kiểm tra {x=6, y=3} vẫn thỏa
điều kiện (x+y<10)^(x>5)^(y≥0) bằng cách chỉ
cần quan tâm đến điều kiện y≥0.
Kĩ thuật tối ưu hệ ràng buộc thứ hai đề
xuất bởi Cristian Cadar và cộng sự được gọi
là kĩ thuật kiểm tra tính thỏa mãn dựa trên bộ

nhớ đệm (cache-based unsatisfiability check).
Kĩ thuật này được áp dụng trong EXE and
KLEE. Theo như tư tưởng kĩ thuật này, tất
cả các hệ ràng buộc đã được giải đều được
lưu lại trong bộ nhớ. Tính luận lý của hệ ràng
buộc kế tiếp được xác định nhanh qua đánh
giá tập các hệ ràng buộc trước đó. Cụ thể,
giả sử trong bộ nhớ lưu hai hệ ràng buộc đã
được giải gồm A, B. Trong đó, hệ ràng buộc
A có nghiệm, hệ ràng buộc B vô nghiệm. Cho

hệ ràng buộc C, tính luận lý hệ ràng buộc này
được kiểm tra nhanh như sau. Nếu A C thì
hệ ràng buộc C có thể có nghiệm, và nghiệm
của A có thể là một phần nghiệm trong C. Nếu
B C thì hệ ràng buộc C vô nghiệm, tức hệ
ràng buộc C không cần đưa vào bộ giải SMTSolver để tìm nghiệm.
Gần đây, Cristian Cadar và cộng sự đã đề
xuất kĩ thuật tối ưu hóa ràng buộc cho mảng
[12], đó là cách chuyển đổi dựa trên chỉ số
(index-based transformation) và chuyển đổi
dự trên giá trị (value-based transformation) để
giảm sự phức tạp của các ràng buộc mảng.
Nghiên cứu này là mở rộng cho công cụ KLEE.
+ Lựa chọn và kết hợp các bộ giải SMTSolver:
Một trong những thách thức chính của
việc thực thi tượng trưng là giải hệ ràng buộc.
Mỗi bộ giải đều có điểm mạnh khác nhau và
chỉ giải có hiệu quả một số ràng buộc. Chẳng
hạn các công cụ EXE, KLEE, Rwset sử dụng

STP-Solver (Simple Theorem Prover) [2] [11];
công cụ Pex, CFT4Cpp sử dụng Z3-Solver;
công cụ DART, SMART [13], CUTE sử dụng
lp_solver; CREST sử dụng Yices-Solver,
SAGE sử dụng Disolver, CFT4CUnit sử dụng
SmtInterpol v.v.
Cristian Cadar và cộng sự đã đưa ra ý
tưởng kết hợp các SMT-Solvers để cùng giải
hệ ràng buộc nhanh nhất có thể (được gọi là
parallel portfolio solver). Mặc dù công cụ SMTSolver ngày càng phát triển mạnh, tốc độ giải
hệ ràng buộc ngày càng nhanh hơn, tuy nhiên
hiện nay vẫn gặp phải một số thách thức như
SMT-solver chưa hỗ trợ giải các ràng buộc phi
tuyến tính, xử lý biểu thức số thực dấu phẩy
động và các ràng buộc phức tạp.
2.4. Giảm thời gian biên dịch
Chi phí tạo dữ liệu kiểm thử có thể giảm
đáng kể dữ liệu bước biên dịch. Khi một dữ
liệu kiểm thử mới được tìm ra, nó được thực
TẠP CHÍ KHOA HỌC 7
QUẢN LÝ VÀ CÔNG NGHỆ


thi trên môi trường thời gian chạy (runtime
environment) để thu thập các câu lệnh đã đi
qua. Hiện tại, có hai cách phổ biến để thực thi
dữ liệu kiểm thử. Dữ liệu kiểm thử được thực
hiện có thể được lưu trữ trực tiếp trong bộ thực
thi dữ liệu thử (test driver) hoặc trong một tệp
ngoài. Trước đây, với các kiểu dẫn xuất, dữ

liệu kiểm thử được phân tích để tạo ra một mã
nguồn tương ứng nhằm khởi tạo dữ liệu kiểm
thử. Nói cách khác, một trình điều khiển kiểm
thử mới được tạo ra mỗi khi có một bộ dữ liệu
kiểm thử tiếp theo được tạo ra. Tổng thời gian
là thời gian thực hiện ba bước: (i) tạo ra một
trình điều khiển kiểm thử mới, (ii) biên dịch dự
án kiểm thử với trình điều khiển kiểm thử mới,
và (iii) thực thi tệp dữ liệu (tệp dữ liệu có thể
lớn đặc biệt là trong các dự án công nghiệp).
Chỉ DART, CUTE và CREST mới áp dụng một
kĩ thuật để tăng tốc thời gian biên dịch dữ liệu
thử nghiệm. Ý tưởng chính của kĩ thuật này là
lưu trữ tất cả dữ liệu kiểm thử trong một file
bên ngoài và một trình điều khiển kiểm thử
phân tích file này để nạp các giá trị của dữ
liệu kiểm thử khi thực thi [1] [3]. Bằng cách đó,
chỉ có một trình điều khiển kiểm thử duy nhất
được tạo ra dùng chung để xử lý tất cả dữ liệu
kiểm thử thay vì nhiều phiên bản như trong ý
tưởng trước đó. Một ưu điểm lớn của chiến
lược này là trình điều khiển kiểm thử chung
chỉ cần biên dịch một lần.
Tuy nhiên, đề xuất của CREST, DART chỉ
áp dụng cho các chương trình C với các dữ
liệu cơ bản, con trỏ và mảng mà chưa mở rộng
sang các kiểu dữ liệu dẫn xuất của C/C++ (ví
dụ cấu trúc, lớp) hoặc mở rộng sang chồng
toán tử, kế thừa, đa hình của các chương
trình C++.

2.5. Kiểm thử tích hợp
Kiểm thử tích hợp (compositional testing)
đã được đề xuất trong [13][15][16][17][18][14].
Patrice Godefroid và cộng sự đã mở rộng
DART để kiểm thử tích hợp bằng cách đề
xuất thuật toán SMART (Systematic Modular
8 TẠP CHÍ KHOA HỌC

QUẢN LÝ VÀ CÔNG NGHỆ

Automated Random Testing) [13]. Phương
pháp này kiểm thử từng hàm riêng biệt sau
đó tóm tắt mỗi hàm thành một biểu thức logic
mệnh đề với các điều kiện tiên quyết đầu vào
và kết quả đầu ra. Tóm tắt của hàm là phép
tuyển của các tóm tắt con, trong đó mỗi tóm
tắt con là một phép hội của các điều kiện thu
thập được trên mỗi đường thi hành. Các tóm
tắt hàm này được sử dụng để kiểm thử các
hàm mức cao hơn. Từ nghiên cứu mở đầu
này, đã có nhiều cải tiến kỹ thuật kiểm thử
tích hợp, tiêu biểu là các nghiên cứu sau: Thứ
nhất là cải tiến kỹ thuật sinh tóm tắt hàm bằng
công thức logic vị từ cấp 1, sau đó sử dụng
SMT để giải [15]; Tiếp theo là cải tiến sinh tóm
tắt bắt buộc (must sumaries) để kiểm thử tích
hợp khi chương trình có sự tiến hóa [16]. Một
số kỹ thuật khác như sinh đồ thị gọi hàm (call
graph), kĩ thuật Forward Slicing, kỹ thuật tóm
tắt từng khối mã nguồn để lưu trong bộ nhớ,

mỗi đường thi hành được tạo bằng cách ghép
nối các tóm tắt đã được lưu trữ [18], kỹ thuật
loại bỏ các đoạn mã dư thừa (lặp lại) [14]
Các nghiên cứu đã cố gắng cải tiến kỹ
thuật sinh tóm tắt hàm và cải tiến chiến lược
chọn đường thi hành, tuy nhiên cũng còn một
số hạn chế đó là sự phức tạp của các tóm
tắt. Thông thường, một dự án có nhiều hàm
được gọi nhau, mỗi hàm được tóm tắt thành
một biểu thức logic, do đó dự án sẽ sinh ra
biểu thức logic cồng kềnh và việc giải quyết
bài toán sẽ trở lên phức tạp. Một vấn đề đặt ra
cần phải có kỹ thuật tối ưu hóa tóm tắt hoặc
cải tiến bộ giải để tăng tốc độ kiểm thử.

III.ĐỀ XUẤT MỘT SỐ HƯỚNG NGHIÊN
CỨU CẢI TIẾN
Từ các phân tích tổng quan về các
phương pháp sinh dữ liệu kiểm thử tự động
từ mã nguồn hiện nay trên thế giới và những
ưu, nhược điểm của chúng, bài báo đề xuất
một vài hướng nghiên cứu cải tiến như sau:
o Một là: Cải thiện chiến lược tìm kiếm


theo chiều rộng đã đề xuất trong DART [3] để
sinh ra ít bộ dữ liệu đầu vào hơn nhưng vẫn
đạt được độ phủ cao. Cố gắng tăng độ phủ
mã nguồn với chiến lược chọn đường thi hành
hiện tại, sinh dữ liệu kiểm thử tĩnh với kỳ vọng

rằng sẽ có nhiều nhánh được phủ sớm hơn.
Mở rộng kỹ thuật biên dịch dữ liệu kiểm thử
trong DART, hiện chỉ hỗ trợ các dự án được
viết bằng C, sang sinh dữ liệu kiểm thử cho
các dự án C ++. Cụ thể là tạo ra một bộ thực
thi dữ liệu kiểm thử (test driver) chung cho
C++ mà nó hỗ trợ nhiều kiểu tham số khác
nhau bao gồm các kiểu cơ bản, mảng, con trỏ
và các kiểu dẫn xuất.
o Hai là: Cải tiến kỹ thuật sinh tóm tắt
hàm và chiến lược chọn đường thi hành để để
tăng tốc độ sinh dữ liệu kiểm thử trong kiểm
thử tích hợp. Trong các nghiên cứu gần đây,
tóm tắt hàm được sinh ra dưới dạng một công
thức logic mệnh đề hoặc công thức logic vị từ
cấp 1, tuy nhiên công thức này chưa được tối
ưu. Do đó cần xem xét để loại ràng buộc thừa,
cải tiến kỹ thuật sinh tóm tắt, kỹ thuật giải hệ
ràng buộc hoặc cải tiến chiến lược tìm đường
thi hành hiệu quả hơn dựa vào các nghiên
cứu mới về kỹ thuật thực thi tượng trưng động
[6][5].
o Ba là: Hiện nay các nghiên cứu về phân
tích mã nguồn tập trung nhiều vào việc hỗ trợ
các cú pháp, kiểu dữ liệu cơ bản, mảng của
chương trình C/C++ ở mức đơn vị (hàm) mà
chưa hướng đến dự án C++ hoàn chỉnh. Do
đó, Bài báo sẽ tiếp tục cải tiến kỹ thuật phân
tích mã nguồn để có thể xử lý được các vấn đề
khuôn hình (template), kế thừa (inheritance),

đa hình (polymorphism). Tìm hiểu các nghiên
cứu mới thực về thi tượng trưng trong [5][6]
để cải tiến kỹ thuật phân tích mã nguồn nhằm
xử lý các vấn đề trên. Cố gắng sử dụng các
kỹ thuật phân tích mã nguồn, kỹ thuật chọn
đường thi hành, kỹ thuật tối ưu hóa và giải
hệ ràng buộc trong các nghiên cứu mới để
hướng tới xây dựng hoàn thiện công cụ kiểm
thử tự động cho các dự án C++.

o Ngoài các hướng nghiên cứu trên, có
thể phát triển các chiến lược tìm kiếm mới
có hiệu quả dựa trên các công cụ machine
learning, deep learning, mạng nơ-ron, tìm
kiếm tiến hóa v.v. Tìm hiểu các bộ giải SAT/
SMT-solver (Z3, SMTInterpol, STP, v.v.) để
tìm ra các thế mạnh của mỗi bộ giải. Lựa chọn
hoặc kết hợp các bộ giải để giải nhằm tận
dụng các thế mạnh của chúng với mục tiêu
giảm chi phí về thời gian thực thi và không
gian bộ nhớ.
Để chứng minh tính hiệu quả của cải tiến
trong các hướng nghiên cứu trên, cần phát
triển công cụ kiểm thử tự động cho các dự án
bằng ngôn ngữ lập trình C++
IV.KẾT LUẬN
Bài báo đã trình bày một cách tổng quan
các vấn đề về sinh dữ liệu kiểm thử tự động từ
mã nguồn. Hai kỹ thuật chính được sử dụng
là kiểm thử tĩnh và kiểm thử động định hướng.

Bài báo đã tổng hợp và trình bày được các ưu
và nhược điểm của các nghiên cứu đã được
công bố và đề xuất ba hướng nghiên cứu cải
tiến chính với bài toán sinh dữ liệu kiểm thử tự
động từ mã nguồn cho các dự án C++.
TÀI LIỆU THAM KHẢO
[1].
J. Burnim and K. Sen. 2008.
Heuristics for Scalable Dynamic Test
Generation. In Proceedings of the 2008
23rd IEEE/ACM International Conference on
Automated Software Engineering (ASE ’08).
IEEE Computer Society, Washington, DC,
USA, 443–446.
[2].
Cristian
Cadar,
Daniel
Dunbar, and Dawson Engler. 2008. KLEE:
Unassisted and Automatic Generation of
High-coverage Tests for Complex Systems
Programs. In Proceedings of the 8th USENIX
Conference on Operating Systems Design
and Implementation (OSDI’08). USENIX
TẠP CHÍ KHOA HỌC 9
QUẢN LÝ VÀ CÔNG NGHỆ


Association, Berkeley, CA, USA, 209–224.
[3].

Patrice Godefroid, Nils Klarlund,
and Koushik Sen. 2005. DART: Directed
Automated Random Testing. In Proceedings
of the 2005 ACM SIGPLAN Conference
on Programming Language Design and
Implementation (PLDI ’05). ACM, New York,
NY, USA, 213–223.
[4]. Koushik Sen, Darko Marinov, and Gul
Agha. 2005. CUTE: A Concolic Unit Testing
Engine for C. In Proceedings of the 10th
European Software Engineering Conference
Held Jointly with 13th ACM SIGSOFT
International Symposium on Foundations of
Software Engineering (ESEC/FSE-13). ACM,
New York, NY, USA, 263–272.
[5]. David Trabish, Andrea Mattavelli,
Noam Rinetzky, and Cristian Cadar. 2018.
Chopped Symbolic Execution. InICSE ’18:
40th International Conference on Software
Engineering, May 27-June 3, 2018, Gothenburg,
Sweden.ACM, New York, NY, USA, 11 pages.
/>[6].
Roberto Baldoni, Emilio Coppa,
Daniele Cono D’Elia, Camil Demetrescu, and
Irene Finocchi. 2018. A Survey of Symbolic
Execution Techniques. ACM Comput. Surv.51,
3, Article 50 (May 2018), 39 pages. https://doi.
org/10.1145/318265
[7]. Duc-Anh Nguyen and Pham Ngoc
Hung. 2017. A Test Data Generation Method

for C/C++ Projects. In SoICT ’17: Eighth
International Symposium on Information
and Communication Technology, December
7–8, 2017, Nha Trang City, Viet Nam. ACM,
New York, NY, USA, 8 pages. https://doi.
org/10.1145/3155133.3155144
[8].
T. Su, G. Pu, B. Fang, J. He, J.
Yan, S. Jiang, and J. Zhao. 2014. Automated
Coverage-Driven Test Data Generation Using
Dynamic Symbolic Execution. In 2014 Eighth
10 TẠP CHÍ KHOA HỌC

QUẢN LÝ VÀ CÔNG NGHỆ

International Conference on Software Security
and Reliability (SERE). 98–107.
[9].
Z. Wang, X. Yu, T. Sun, G. Pu, Z.
Ding, and J. Hu. 2009. Test Data Generation
for Derived Types in C Program. In 2009 Third
IEEE International Symposium on Theoretical
Aspects of Software Engineering. 155–162.
[10].
Nicky Williams, Bruno Marre,
Patricia Mouy, and Muriel Roger. 2005.
PathCrawler: Automatic Generation of Path
Tests by Combining Static and Dynamic
Analysis. In Proceedings of the 5th European
Conference on Dependable Computing

(EDCC’05).
Springer-Verlag,
Berlin,
Heidelberg, 281–292
[11].
Cristian Cadar, Vijay Ganesh,
Peter M. Pawlowski, David L. Dill, and
Dawson R. Engler. 2008. EXE: Automatically
Generating Inputs of Death. ACM Trans. Inf.
Syst. Secur. 12, 2, Article 10 (Dec. 2008), 38
pages.
[12]. David M. Perry, Andrea Mattavelli,
Xiangyu Zhang, and Cristian Cadar,
Accelerating array constraints in symbolic
execution, in Proceedings of the 26th ACM
SIGSOFT International Symposium on
Software Testing and Analysis, New York,
USA, 2017, pp. 68-78.
[13]. Patrice Godefroid, Compositional
dynamic test generation, in Proceedings of
the 34th annual ACM SIGPLAN-SIGACT
symposium on Principles of programming
languages, 2007, pp. 47-54.
[14]. Y. Lin, T. Miller and H. Sondergaard,
"Compositional
Symbolic
Execution:
Incremental
Solving
Revisited,"

2016
23rd Asia-Pacific Software Engineering
Conference(APSEC), Hamilton, New Zealand,
2016, pp. 273-280.
doi:10.1109/APSEC.2016.046


[15]. S. Anand, P. Godefroid, and N.
Tillmann, “Demand-driven compositional
symbolic execution” in Tools and Algorithms
for the Construction and Analysis of Systems
(TACAS), ser. LNCS, C. R. Ramakrishnan
and J. Rehof, Eds. Springer, 2008, vol. 4963,
pp. 367–381
[16]. P. Godefroid, S.K. Lahiri, C. RubioGonzález,
"Statically
validating
must
summaries for incremental compositional
dynamic test generation," in Proceedings of
the 18th International Conference on Static
Analysis, pp. 112-128, 2011.

[17]. M. Christakis and P. Godefroid,
“IC-Cut: A compositional search strategy for
dynamic test generation,” Proceeding SPIN
2015 Proceedings of the 22nd International
Symposium on Model Checking Software Volume 9232 Pages 300-318
[18]. Y. Lin, T. Miller, H. Sandergaard,
"Compositional symbolic execution using finegrained summaries", Proc. 24th Australasian

Softw. Eng. Conf IEEE, pp. 213-222, 2015.

TẠP CHÍ KHOA HỌC 11
QUẢN LÝ VÀ CÔNG NGHỆ



×