由2.2節的並行程式之行為可知,測試一個並行 Java 程式,等於測試 main() 函式的所有展開的 expanded paths 與 concurrent paths。若直接測試這些展開的 expanded paths 及 concurrent paths,則有兩個缺點:(1)concurrent path 含有數個 expanded paths, expanded path 又含有多個函式的 path,錯誤發生時,難以鑑別 發生錯誤的指令位置;(2)同一個函式的 path 可能會出現在許多不同 expanded paths,將被重複測試多次。因此可運用 bottom-up 測試的觀念,來降低測試之複 雜度與時間。
在 main()函式或主動類別的 run()函式的 expanded path 中,若沒有建立新的 thread,這條 expanded path 可視為循序執行,可用循序 Java 的測試方法測試之,
因此並行 Java 程式可先測試其循序執行性質,再測試其並行性質,其測試方法 可為四個步驟[6]:
(1) 以 main()函式或主動類別的 run()函式為單位,根據 Java 程式的結 main()
AC1
run()
AC3
run()
AC2
run()
構模型,找出 main()函式或 run()函式使用到的所有類別。
(2) 忽略呼叫 Thread 物件的 start()函式的指令,以2.1節介紹之循序 Java 程式的 bottom-up 測試方法驗證 main()函式或 run()函式使用到 的類別,最後循序測試 main()函式或 run()函式每一條 expanded path。
(3) 以2.2節描述的方法組合被 main()函式用來建立 thread 的主動類 別 run()函式的 expanded paths,建構 main()函式所有的 concurrent paths。
(4) 依序測試每一條 concurrent path,驗證每一條 concurrent path 的執 行行為。
上述步驟(1)與步驟(2)主要在驗證所有 main()及 run()函式的循序的執行行 為,與測試所有的 expanded paths 相比,有較低的測試複雜度與偵錯的成本,且 可必免重複測試。
main()函式或主動類別的 run()函式可能有多條 expanded path,當一條 expanded path 執行時有呼叫 Thread 物件的 start()函式,會產生一個 thread 以執行 該 Thread 物件的 run()函式,當給定一輸入資料以執行 main()函式的這條 expanded path,此 expanded path 所產生的其它 threads 將各自執行一條 expanded path,它 們的組合即為一條 concurrent path,如圖2-13,main()的一條 expanded path 利 用呼叫 Thread 物件的 start()函式產生了兩個新的 thread,依上節對執行個體的命 名稱為 Run A 與 Run B,而 Run A 與 Run B 各有三條 expanded paths。main()函 式 expanded path 的輸入資料不同,Run A 與 Run B 也會執行不同的 expanded path,因此構成不同的 concurrent path。因此只要 main()函式的 expanded path 含 有一個或多個 start()指令,將會產生多條 concurrent path;由圖2-13之範例,
Run A 與 Run B 各有三條 expanded path,每一個 Run 的 expanded path 均應被測 試過,且每一 shared object 的交互行為也均應被測試過,如何選擇最少組合數目 來完成上述兩個目的,即為本研究的主題。
圖2-13 並行 Java 程式與存取 shared object 範例
當執行一含有 shared object 的 concurrent path 時,當 thread 之間相對執行速 度不一時,各 thread 呼叫執行 shared object 的先後順序亦可能不同,因此可能產 生不同結果,此即為 concurrent path 之不確定執行行為[7]。如圖2-13中 Run A 與 Run B 共享 Count 這個 shared object,假設 value 的初值為 0,若三個訊息到達 的順序為 A’s inc()ÆB’s inc()ÆA’s get(),則 Run A 呼叫 get()所得到的值為 2;若 順序為 A’s inc()ÆA’s get()ÆB’s inc(),則 Run A 呼叫 get()所得到的值為 1。因此 測試一個含有 shared object 的 concurrent path 時,需測試每種存取 shared object 的順序,才能夠達到驗證此 concurrent path 所有行為之目的。現有的一些測試工 具,如 BugNet [8]或 TAP[9],均提供控制各 thread 間執行順序的機制,因此可以 控制 shared object 訊息到達的順序,要測試時,需先列舉所有可能存取 shared object 的順序,控制訊息到達順序,才能得到預期目的[6]。
在圖2-13的範例中,驗證此並行程式所有的行為,應測試過所有九條可 能的 concurrent path,分別記為 A1B1、A1B2、A1B3、A2B1、A2B2、A2B3、
A3B1、A3B2、A3B3。其中 A2 與 B2 因有 shared object 使這兩條 paths 會互相影 main()
Run A Run B
Count int value;
synchronized void inc() {
++value;
} synchronized int get()
{
響,因此需以 A2B2 這條 concurrent path 測試它們的互動情形,其它使用到這兩 條 paths 其中之一的 concurrent path,如 A1B2 執行了 B2,因為 A1 與 B2 間沒有 互動,這兩條 paths 僅會獨立循序執行,而 B2 獨立執行的執行行為已在 concurrent path A2B2 中被測試過,只要測試另一可包含 A1 的循序執行行為的 concurrent path,如 A1B1,則 concurrent path A1B2 就不需測試,因為它的執行行為都已被 測試過。在此例中若選出 A1B1、A2B2、A3B3 這三條 concurrent paths 來測試,
即可測試到 A2 與 B2 互相影響的情況,同時亦可測試到所有六條 paths 循序執行 的執行行為,因此其它 concurrent paths 便可以不必測試亦可保證其行為已被驗 證。由此可知,對於並行 Java 程式,可以測試部分的 concurrent paths 即可驗證 所 有 的 執 行 行 為 , 因 此 有 必 要 找 到 一 個 系 統 化 的 方 法 來 自 動 產 生 最 少 的 concurrent paths 加以測試。
第3章 Concurrent Path 的選取方法
執行並行 Java 程式將有多個 threads 同時執行,且這些 threads 間有訊息交換 而影響其執行過程及結果,在進行並行 Java 程式測試,必須完整測試所有 threads 間互動情況,才能保證其正確性;如何選擇最少的測試個案來驗證所有並行執行 thread 間的互動關係,以降低測試成本,目前尚無良好方法,因此只能用暴力法 (exhaustive)來產生所有 concurrent paths 而加以測試。並行執行的 threads 間主要 是透過 shared object 來交換資料,本章首先分析存取同一 shared object 的 path 的 資料流關係,找出 concurrent path 之不同 path 間之相互影響關係,進而找出需並 行測試的不同 thread 的 paths 的最少組合,利用這些組合去產生所需的最少 concurrent paths。
本章3.1節說明不同 thread 之 paths 間存取 shared object 而產生之依存的關 係;3.2節提出找尋有依存關係 path 組合之方法;3.3提出找出涵蓋所有需測 試之 path 組合之 concurrent paths 之方法。