CHƢƠNG 2 TỔNG QUAN VỀ HỆ THỐNG NHÚNG
3.2. Hệ thống với các mô hình hữu hạn
3.2.2. Thăm dò và phân tích chƣơng trình mô hình hữu hạn
3.2.2.1. Máy trạng thái hữu hạn (FSM-Finite State Machines)
Theo tài liệu [4], FSM đƣợc định nghĩa nhƣ sau: “Một FSM chỉ đơn giản là một bộ sưu tập hữu hạn các phép chuyển trạng thái. Các thành phần trong FSM gồm có: đối số đầu tiên là trạng thái khởi tạo, đối số tiếp theo là một danh sách các trạng thái chấp nhận, và đối số cuối cùng là một danh sách các phép chuyển trạng thái. Trong đó, mỗi phép chuyển trạng thái là một bộ ba: trạng thái hiện tại, hành động (nhãn chuyển tiếp), và trạng thái tiếp theo”.
Các đƣờng chạy và các kịch bản (Runs and scenarios)
Mỗi FSM mô tả tất cả những đƣờng chạy mà có thể đạt đƣợc bằng cách đi qua một đƣờng xung quanh các nút và các liên kết trong đồ thị của nó. Đây là một mô tả phi hình thức của thuật toán để tạo ra một đƣờng chạy: Bắt đầu trong trạng thái khởi tạo. Trong một trạng thái mà có những chuyển tiếp sang các trạng thái tiếp theo, lựa chọn bất kỳ trạng thái nào. Nếu một trạng thái đạt đến mức không có chuyển tiếp đi thì
đƣờng chạy kết thúc trong trạng thái đó. Hoặc, nếu trạng thái đã đƣợc chỉ định một trạng thái chấp nhận thì đƣờng chạy kết thúc trong trạng thái chấp nhận đó.
Khái niệm kịch bản theo tài liệu [4]: “Một bộ sưu tập các đường chạy liên quan được gọi là một kịch bản. Toàn bộ bộ sưu tập các đường chạy có thể được sinh từ một FSM được gọi là kịch bản được định nghĩa bởi FSM”.
FSMs kịch bản và FSM đúng (the true FSM)
Khái niệm FSMs kịch bản theo tài liệu [4]: “Một FSM mà mục đích của nó là để định nghĩa một kịch bản đƣợc gọi là một FSM kịch bản”. Nó có thể định nghĩa nhiều FSMs kịch bản khác nhau cho bất kỳ MP nào; mỗi FSM kịch bản khác nhau định nghĩa một bộ sƣu tập các đƣờng chạy khác nhau. Có một FSM có thể tạo ra tất cả các đƣờng chạy của một MP đó là FSM đúng của nó. Khi MP có một số lƣợng trạng thái và hành động nhỏ, nó có thể viết ra FSM đúng của nó.
3.2.2.2. Thăm dò
Việc thăm dò sẽ tự động sinh ra một FSM từ một chƣơng trình mô hình. FSM có thể đƣợc sử dụng để trực quan hoá, phân tích và tạo test ngoại tuyến. Trong phần này, cách thăm dò một MP với thƣ viện và các công cụ sẽ đƣợc giải thích.
Việc truy cập chƣơng trình mô hình:
Trƣớc hết, xét ví dụ về phƣơng thức và lớp factory của một MP NewsReader nhƣ sau:
public static class Factory {
public static ModelProgram Create() {
return new LibraryModelProgram(typeof(Factory).Assembly,
"NewsReader"); }
}
Để làm việc với thƣ viện hoặc bất kỳ công cụ nào tạo ra nó (gồm có mpv, otg, hoặc ct) thì một MP phải cung cấp một phƣơng thức factory tạo ra một đối tƣợng
LibraryModelProgram từ chƣơng trình mô hình đƣợc biên dịch. Tất cả các công cụ truy cập MP gián tiếp bằng cách gọi các phƣơng thức của đối tƣợng này.
Phƣơng thức factory nên đƣợc đặt vào trong lớp factory của riêng nó. Lớp factory luôn luôn có cùng hình thức, chỉ có một vài định danh phải đƣợc thay đổi cho mỗi MP cụ thể. Lớp factory phải đƣợc khai báo public static. Các lớp chứa biến trạng thái và các phƣơng thức hành động của MP không cần public mà chúng mặc định là truy cập private. Tên lớp factory thƣờng đặt là Factory và phƣơng thức factory đặt là
Create. Trong phần thân của phƣơng thức factory, tại hàm tạo LibraryModelProgram
đối số của toán tử typeof phải cùng lớp. Đối số thứ hai là một chuỗi chứa tên của không gian tên MP.
Để gọi một công cụ trên một MP, tham chiếu hội đồng chứa MP đƣợc biên dịch và cung cấp tên đầy đủ của phƣơng thức factory trong hội đồng đó. Ví dụ lệnh gọi
mpv /r:NewsReaderUI.dll NewsReader.Factory.Create
Thăm dò và trực quan hoá hoàn toàn (Exhaustive exploration and visualization):
Khi công cụ mpv đƣợc gọi trên một chƣơng trình mô hình hữu hạn, nó thăm dò hoàn toàn MP, tạo ra FSM đúng của nó và hiển thị đồ thị chuyển trạng thái của nó.
Mỗi nút trong đồ thị đƣợc dán nhãn bằng các giá trị của tất cả các biến trạng thái trong trạng thái đó. Điều này không đƣợc phổ biến cho nhiều biến trạng thái nên mpv chỉ đơn giản là dán nhãn mỗi nút với một số. Trạng thái khởi tạo luôn luôn đƣợc gán là số 0 còn các nút khác là số tùy ý. Công cụ mpv cung cấp một bảng điều khiển quan sát trạng thái để chỉ ra tất cả các biến trạng thái và giá trị của chúng trong nút đƣợc chọn. Do đó, mpv đƣợc sử dụng nhƣ là một công cụ gỡ lỗi cho MP.
Việc thăm dò và quan sát MP trong khi phát triển có thể kiểm tra thƣờng xuyên xem nó có hoạt động nhƣ ý định hay không. Ngay khi một vài hành động đƣợc viết mã (code) thì đã có thể bắt đầu việc thăm dò.
Thăm dò tƣơng tác và mô phỏng:
Công cụ mpv cũng có thể thăm dò tƣơng tác, thực hiện và hiển thị một vài chuyển tiếp tại một thời điểm dƣới sự điều khiển của ngƣời dùng. Việc thăm dò tƣơng tác tạo nên sự thuận tiện hơn để mô phỏng các đƣờng chạy cụ thể. Mô phỏng để kiểm tra xem đƣờng chạy thành công hay bị chết.
Thuật toán thăm dò:
Trong phần này, cách làm việc của việc thăm dò sẽ đƣợc giải thích bằng một thuật toán (hình 3.3). Để sử dụng công cụ mpv khi phân tích các MP hữu hạn thì không cần phải hiểu thuật toán. Nhƣng nếu phân tích các MP vô hạn thì phải cung cấp thêm thông tin để hƣớng dẫn thăm dò. Việc thăm dò tạo ra một FSM từ một MP, tự động lựa chọn các hành động để thực hiện, giám sát và ghi chép mỗi chuyển trạng thái khi nó xảy ra.
State: Trạng thái của một MP đƣợc biểu diễn bởi một từ điển kết hợp mỗi tên biến trạng thái với giá trị của nó.
Action: Lời gọi của một hành động gồm có tên phƣơng thức hành động và tất cả đối số của nó.
Transition: Một chuyển trạng thái với hàm tạo Transition(State current, Action a, State next)
Trong thuật toán, lớp ModelProgramSimple cũng cung cấp những thuộc tính và phƣơng thức sau:
State InitialState: Thuộc tính trả về trạng thái khởi tạo của MP.
Set<Action> GetActions(State s): Phƣơng thức trả về tập hợp hành động đƣợc kích hoạt trong trạng thái s.
State GetTargetState(State s, Action a): Phƣơng thức trả về trạng thái tiếp theo đạt đƣợc bằng cách thực hiện hành động a trong trạng thái hiện
Hình 3.3: Một thuật toán thăm dò đầy đủ
Set<T>: Bộ không theo thứ tự của các phần tử có kiểu T, với các hàm tạo Set<T>(), Set<T>(x,y,z), và phƣơng thức Add, mà trong đó s.Add(x)
trả về một tập hợp mới có chứa tất cả các phần tử của tập s và phần tử x.
Sequence<T>: Bộ có thứ tự các phần tử thuộc kiểu T, với các thuộc tính Head (phần tử đầu tiên), Tail(tất cả nhƣng trừ phần tử đầu tiên), và phƣơng thức AddFirst (đẩy một phần tử lên đầu), và AddLast (nối thêm một phần tử vào cuối).
Hình 3.3 chỉ ra một thuật toán thăm dò hoàn toàn. Frontier là bộ các trạng thái đã đạt đƣợc nhƣng các chuyển tiếp đƣợc kích hoạt của nó chƣa đƣợc thực hiện. Khi việc thăm dò bắt đầu, chỉ có trạng thái khởi tạo là thuộc vào tập Frontier. Việc thăm dò tiếp tục (proceed) bằng cách thực hiện các chuyển tiếp đƣợc kích hoạt trong Frontier. Khi thực hiện, một chuyển tiếp đạt đƣợc một trạng thái mà trƣớc đó chƣa đạt đƣợc, trạng thái đó đƣợc thêm vào tập Frontier. Khi tất cả các chuyển tiếp đƣợc kích hoạt trong một trạng thái đã đƣợc thực hiện, trạng thái đó đƣợc gỡ bỏ từ tập Frontier và đƣợc thêm vào bộ sƣu tập các trạng thái đã đƣợc thăm dò. Thuật toán kết thúc khi tập Frontier rỗng (Chú ý rằng nếu MP là hữu hạn thì thuật toán luôn luôn có kết thúc).
3.2.2.3. Phân tích
Việc phân tích nhằm phát hiện ra các lỗi thiết kế của các hệ thống. Có hai loại phân tích là phân tích an toàn (Safety analysis) và phân tích hoạt động đƣợc (liveness).
Phân tích an toàn
Theo tài liệu [4]: “Phân tích an toàn là kiểm tra bất cứ điều gì xấu có thể xảy ra. Nó tìm kiếm các trạng thái không an toàn vi phạm các yêu cầu an toàn. Các yêu cầu đƣợc thể hiện bằng biểu thức Boolean đƣợc gọi là một bất biến (invariant) mà bất biến đƣợc cho là đúng trong mọi trạng thái có thể đạt đƣợc.”
Theo tài liệu [4], “Một trạng thái không an toàn là một trạng thái mà một bất biến là sai. Đối với các công cụ, một bất biến là một trƣờng, thuộc tính hoặc phƣơng thức Boolean với thuộc tính [StateInvariant].”
Phân tích an toàn phụ thuộc vào việc viết một bất biến diễn tả thuộc tính an toàn mà ta muốn kiểm tra. Cần phải xem xét đâu là những trạng thái đƣợc phép (tức trạng thái làm cho bất biến đúng) và những trạng thái bị cấm (tức trạng thái làm cho bất biến sai). Việc xác định các trạng thái an toàn đòi hỏi sự phán đoán và sự hiểu biết mục đích của chƣơng trình.Việc thăm dò có thể tìm tất cả các trạng thái không an toàn trong FSM đã đƣợc tạo ra.
Phân tích tính hoạt động đƣợc
Theo tài liệu [4] thì “Phân tích tính hoạt động đƣợc là kiểm tra xem có bất cứ điều gì tốt sẽ xảy ra. Nó tìm kiếm các trạng thái chết mà từ trạng thái đó mục tiêu không thể đạt đƣợc. Các yêu cầu hoạt động đƣợc thể hiện bằng cách xác định các trạng thái chấp nhận mà tại đó mục tiêu đạt đƣợc. Việc xác định các trạng thái chấp nhận bằng cách viết một biểu thức điều kiện Boolean mà biểu thức này luôn đúng trong mọi trạng thái chấp nhận.”
Theo tài liệu [4] thì “Một trạng thái chết là một trạng thái mà từ đó một trạng thái chấp nhận không thể đạt đƣợc”. Đối với các công cụ, một điều kiện trạng thái chấp nhận là một trƣờng, thuộc tính hoặc phƣơng thức Boolean đƣợc dán nhãn với thuộc tính [AcceptingStateCondition].
Phân tích tính hoạt động đƣợc phụ thuộc vào việc viết một điều kiện trạng thái chấp nhận mà mục tiêu chƣơng trình có ý định đạt đƣợc. Một trạng thái chấp nhận thƣờng đƣợc định nghĩa là một trạng thái mà chƣơng trình đƣợc phép dừng lại. Tuy nhiên, nhiều chƣơng trình có bộ điều khiển nhúng không bao giờ dừng lại. Để sử dụng phân tích tính hoạt động đƣợc với hệ thống nhƣ vậy thì định nghĩa phải đƣợc mở rộng. Khi đó, một trạng thái chấp nhận đƣợc định nghĩa là một trạng thái mà mục đích chƣơng trình đạt đƣợc. Trong đó, một vài đơn vị làm việc hay tác vụ đƣợc hoàn thành, không có phần công việc hay tác vụ bị bỏ dở.
3.2.3. Cấu trúc chƣơng trình mô hình với thành phần (Composition)
Cơ chế để cấu trúc các MP ở quy mô lớn đó là thành phần (composition). Cơ chế này rất linh hoạt nên có thể đƣợc sử dụng theo nhiều cách. Nó đƣợc sử dụng để
3.2.3.1. Điều khiển kịch bản (Scenario control)
Theo tài liệu [4] thì “Vấn đề giới hạn việc phân tích và kiểm thử các đƣờng chạy cụ thể đƣợc quan tâm đƣợc gọi là điều khiển kịch bản”.
Điều khiển kịch bản là cần thiết vì một MP hành động thƣờng đƣợc viết nhƣ một bản đặc tả hoặc bản hợp đồng (contract). Do đó, nó mô tả tất cả mọi thứ mà Impl phải làm, có thể làm và không thể làm. Kết quả là MP thƣờng mô tả một số lƣợng lớn các đƣờng chạy. Khi phân tích và đặc biệt là khi kiểm thử, các kiểm thử viên thƣờng không muốn xem xét tất cả các đƣờng chạy này mà họ muốn giới hạn việc xem xét của mình với một kịch bản cụ thể (gồm có một bộ sƣu tập các đƣờng chạy có lẽ chỉ có một đƣờng là thích hợp).
3.2.3.2. Thành phần (Composition)
Khái niệm thành phần [4]: “Thành phần là việc kết hợp các chƣơng trình mô hình riêng biệt vào một MP mới đƣợc gọi là sản phẩm (product). Sản phẩm đƣợc hình thành bằng cách soạn các MP M1 và M2 đƣợc viết M1xM2 hoặc M1*M2.”
Thành phần đƣợc thực hiện tự động bởi các công cụ phân tích và kiểm thử khi nhiều MP đƣợc đặt tên trên dòng lệnh. Sau đó, các công cụ có thể phân tích hoặc kiểm thử từ sản phẩm. Thành phần đƣợc sử dụng cho bản điều khiển kịch bản. Sau đây, là phần giải thích cách mà thành phần đƣợc thực hiện và mô tả một số kiểu bổ sung cho việc viết mã các MP làm cho nó dễ dàng để viết các kịch bản cho thành phần.
Thành phần có thể hiểu đƣợc (Understanding composition)
Thành phần kết hợp các MP riêng biệt có không gian tên riêng biệt và có thể đƣợc biên dịch vào các hội đồng riêng biệt. Bất kỳ số lƣợng MP nào đều đƣợc chấp nhận bởi tất cả các công cụ.
Sản phẩm của hai hoặc nhiều MP có tất cả các biến trạng thái và tất cả hành động của mỗi chƣơng trình đƣợc biên soạn. Ý tƣởng chính trong thành phần là: các hành động đƣợc chia sẻ có cùng tên trong hai hoặc nhiều chƣơng trình đƣợc biên soạn là một hành động trong sản phẩm. Chúng thực hiện cùng nhau, và có thể chỉ thực hiện khi tất cả các hành động đồng thời đƣợc kích hoạt trong mỗi chƣơng trình của chúng.
Yêu cầu trong thành phần là các hành động đƣợc chia sẻ phải đƣợc kích hoạt đồng thời trong mọi chƣơng trình đƣợc biên soạn, chúng xảy ra thƣờng xuyên có tác dụng giới hạn hành vi và loại bỏ một vài đƣờng chạy. Đó là lý do tại sao thành phần là hữu ích cho điều khiển kịch bản.
Tuy nhiên, có một ngoại lệ quan trọng là: Các hành động không đƣợc chia sẻ có thể xen kẽ vào thứ tự bất kỳ trong sản phẩm, và có thể thực hiện bất cứ khi nào chúng đƣợc kích hoạt trong các chƣơng trình riêng của chúng. Nếu muốn ngăn chặn việc xen kẽ này thì phải thêm những hành động đó tới các MP khác và vô hiệu hóa các hành động đó ở mọi trạng thái trong các MP khác.
Xét một ví dụ nhỏ sau: M1 và M2 là các MP, M1 có các hành động A() và B(2) tạo nên các chuyển tiếp từ trạng thái khởi tạo 0 đến trạng thái 1 và đến trạng thái chấp
thái 0 đến 1 và lặp lại, ở đây trạng thái 0 vừa là trạng thái khởi tạo vừa là trạng thái chấp nhận (hình 3.5). Sản phẩn của chúng là M1xM2 xuất hiện trong hình 3.6. Tất cả 3 hành động xuất hiện trong sản phẩm.
Hình 3.4: Chƣơng trình mô hình M1
Tiếp theo, là phần giải thích cách tạo thành FSM của sản phẩm M1xM2 (kết hợp từ M1 và M2) đã thu đƣợc trong hình 3.6 nhƣ sau:
Có một phƣơng thức có tính hệ thống để tạo sản phẩm của hai MP. Đầu tiên, xác định từ vựng hành động (action vocabulary) cho mỗi chƣơng trình và những hành động không đƣợc chia sẻ từ chƣơng trình khác. Trong ví dụ này, đối với M1 có từ vựng hành động là A, B và một hành động không đƣợc chia sẻ là C. Đối với M2 có từ vựng hành động là B, C và một hành động không đƣợc chia sẻ là A. Sau đó, bƣớc tiếp theo là hình thành mở rộng vòng lặp (loop extension) cho mỗi chƣơng trình bằng cách: Tại mỗi trạng thái, thêm vào một chuyển tiếp vòng lặp với chính nó cho mỗi hành động không đƣợc chia sẻ. Trong các mở rộng vòng lặp, các hành động có cùng ký tự hành động (tức các hành động có cùng tên) đƣợc tạo để có cùng arity (tức số lƣợng tham số) bằng cách mở rộng chúng với các tham số gữ chỗ (placehoder) bởi dấu gạch dƣới “_”. Ví dụ hành động B(2) từ M1 có tham số là 2 còn hành động B() ở M2 có cùng tên nhƣng chƣa có tham số sẽ trở thành B( _ ) trong phần mở rộng vòng lặp của M2, vì vậy nó có cùng arity với B(2) trong M1. Hình 3.7 và hình 3.8 chỉ ra các phần mở rộng vòng lặp của M1 và M2 tƣơng ứng. Sản phẩm đƣợc tạo nên từ các mở rộng vòng lặp này.
Hình 3.5: Chƣơng trình mô hình M2
Hình 3.8: Phần mở rộng vòng lặp của M2
Mỗi trạng thái trong sản phẩm là một trạng thái đôi đƣợc hình thành từ một trạng thái trong M1 và một trạng thái trong M2 (hình 3.9). Hình thành trạng thái đôi khởi tạo (0,0) từ các trạng thái khởi tạo của M1 và M2. Tạo ra phần còn lại của sản