1. Trang chủ
  2. » Công Nghệ Thông Tin

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

9 65 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 9
Dung lượng 309,96 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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ài viết 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.

Trang 1

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: huongtw@gmail.com

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.

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

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

Trang 2

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

SMT-solver) 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

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

Trang 3

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

Trang 4

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

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

Trang 5

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 SMT-Solver để 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 SMT-Solver:

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ụ SMT-Solver 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

Trang 6

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

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

Trang 7

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

Trang 8

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

https://doi.org/10.1145/3180155.3180251

[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

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

Trang 9

[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

Rubio-Gonzá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 fine-grained summaries", Proc 24th Australasian Softw Eng Conf IEEE, pp 213-222, 2015

Ngày đăng: 25/10/2020, 03:21

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w