[OO] Unit3 Summary JML系列

[OO] Unit3 Summary JML系列

JML理論基礎

優點

  • 對接口行爲、功能和規格作出描述和規定,用數學語言描述能夠有效確保設計者的本意被清晰表達,沒有二義性
  • JML將自然語言轉化爲機器能夠理解並執行的語言,這爲自動的自動化測試提供支撐
  • 理論上,基於規格存在一種完美的自動化測試,能夠有效根據JML規格進行自動的數據生成與正確性檢驗,並且這樣的測試是完全基於規格的,因此是完全準確的

語法基礎

常用表達式

  • \result表達式:表示一個非 void 類型的方法執行所獲得的結果,即方法執行後的返回值。

    \result == null;

  • \old( expr )表達式 :用來表示一個表達式 expr 在相應方法執行前的取值。

    people.length == \old(people.length);

  • \not_assigned(x,y,…)表達式 :用來表示括號中的變量是否在方法執行過程中被賦值。如果沒有被賦值,返回爲 true ,否則返回 false

    (\forall int i; 0 <= i < groups.length; \not_assigned(groups[i]));

  • \forall表達式 :全稱量詞修飾的表達式,表示對於給定範圍內的元素,每個元素都滿足相應的約束。

    (\forall int i; 0 <= i < groups.length; \not_assigned(groups[i]));

  • \exists表達式 :存在量詞修飾的表達式,表示對於給定範圍內的元素,存在某個元素滿足相應的約束。
    例:

    (\exists int i; 0 <= i && i < people.length && people[i].getId() == id2;
    money[i] == \old(money[i] + value));

  • \sum表達式 :返回給定範圍內的表達式的和。

    (\sum int i; 0 <= i < people.length && people[i].getAge() >= l && people[i].getAge() <= r; 1);

方法規格

  • 前置條件:通過requires子句來表示: requires P 其中requires是JML關鍵詞,表達的意思是“要求調用者確保P爲真”。方法規格中可以有多個requires子句,是並列關係,即調用者必須同時滿足所有的並列子句要求。
  • 後置條件:通過ensures子句來表示,是對方法執行結果的限制,如果執行結果滿足後置條件,則表示方法執行正確,方法規格中可以有多個ensures子句,爲並列關係,實現者必須同時滿足有所並列ensures子句的要求。
  • 副作用 :副作用指方法在執行過程中會修改對象的屬性數據或者類的靜態成員數據,從而給後續方法的執行帶來影響。從方法規格的角度,必須要明確給出副作用範圍。
  • public normal_behavior:方法的正常處理部分。
  • public exceptional_behavior:方法的異常處理部分。
  • signals:後加表達式,拋出某異常以及拋出異常的條件語句爲該表達式,當表達式爲真時拋出異常。
  • signals_only:強調滿足前置條件時候拋出相應異常。

類型規格

  • 不變式(invariant):是要求在所有可見狀態下都必須滿足的特性,語法上定義 invariant P ,其中invariant 爲關鍵詞, P 爲謂詞。
  • 狀態變化約束(constraint):對前序可見狀態和當前可見狀態的關係約束,invariant和constraint可以直接被子類繼承獲得。

JML工具鏈

不得不說,JML的工具鏈是真的難用,生態糟糕

  • OpenJML : JML語法檢查

  • JMLUnitNG :自動生成測試樣例覆蓋性測試所有方法,僅使用邊界數據

  • 其他工具:

    ESC/Java2 1, an extended static checker which uses JML annotations to perform more rigorous static checking than is otherwise possible.

    Daikon, a dynamic invariant generator.

    KeY, which provides an open source theorem prover with a JML front-end and an Eclipse plug-in (JML Editing) with support for syntax highlighting of JML.

    Krakatoa, a static verification tool based on the Why verification platform and using the Coq proof assistant.

    JMLEclipse, a plugin for the Eclipse integrated development environment with support for JML syntax and interfaces to various tools that make use of JML annotations.

    Sireum/Kiasan, a symbolic execution based static analyzer which supports JML as a contract language.

    TACO, an open source program analysis tool that statically checks the compliance of a Java program against its Java Modeling Language specification.

    VerCors verifier

測試工具使用報告

SMT Solver

  • 報錯太多,給定的JML需要進行大量修改,此處省略

EvoSuite

  • 這是一個較新的自動生成基於Junit的測試代碼的工具
  • 將evosuite進行部署後,在希望測試的類上點擊Run EvoSuite在這裏插入圖片描述
  • 指定生成樣例時使用的處理器數量,以及jdk版本和用於生成測試樣例的時間後,開始生成在這裏插入圖片描述
  • 得到如下JUnit測試文件,此處僅以MyNetwork爲例,其中前兩個爲EvoSuite自動生成的,後一個是我自己手用JUnit4寫的測試
    在這裏插入圖片描述在這裏插入圖片描述
  • 測試代碼舉例如下,演示使用的代碼僅生成了66個測試樣例,增加生成時間能夠提高覆蓋率在這裏插入圖片描述
  • 除此之外,其對邊界數據的覆蓋情況也很好,以test61爲代表在這裏插入圖片描述
  • 進行測試後結果如下在這裏插入圖片描述
  • 可以看到,方法覆蓋率達到了100%,line覆蓋率很高83%,可以結合下文方法查漏補缺,(這個測試文件只是MyNeywork的)在這裏插入圖片描述
  • 下面展示對全部工程文件的測試情況,生成測試代碼時指定了使用6個core,CPU佔用率基本拉滿在這裏插入圖片描述
  • 測試結果和覆蓋率如下在這裏插入圖片描述
    在這裏插入圖片描述
  • 可以看到覆蓋率近乎完美,除了MyNetwork的Line覆蓋率83%,對此可以採用提高生成測試代碼時間進行彌補,或根據左側標紅(代表未覆蓋)進行手動構造測試數據,加入到測試代碼中在這裏插入圖片描述

JMLUnitNG

  • 以測試Group.java的實現是否正確爲例,執行如下命令java -jar jmlunitng.jar -cp spec3.jar MyGroup.java,得到如下文件在這裏插入圖片描述
  • 導入相關依賴包後進行測試,以下是部分測試結果在這裏插入圖片描述
    在這裏插入圖片描述
  • 可以看到其主要是針對邊界數據進行測試,尤其是NULL的情況
  • 以上7個Failures均在null傳入參數遇到,相對於EvoSuite而言JMLUnitNG的測試更多是在邊界數據,能夠找到更多的邊界樣例,分析其原因可以發現,addPerson和delPerson並沒有給出JML規格,所以纔會產生NULL的問題在這裏插入圖片描述
  • 將規格進行補全後,所有的測試數據均爲Passed
  • 對比EvoSuite和JMLUnitNG可以看到,EvoSuite能夠產生更多的有意義的實際數據,有些bug是需要這種數量較多才能體現出來的,例如Group中的相關緩存量的更新bug,JMLUnitNG無論如何也測試不出來
  • 因此最好的做法是將兩者進行結合

黑盒測試

  • 最後,筆者同時使用了黑盒測試的方法進行對拍,基於大量複雜數據的測試能夠發現一些出現率極低的bug
  • 三種測試方法結合使用效果最佳

架構設計

  • 採用將最短路單獨包裝、雙連通算法包裝、並查集單獨包裝的外觀,各自管理所需的數據結構在這裏插入圖片描述
  • 關於採用的算法與時間複雜度的可行性分析由此可以說明
  • JML系列 優化及時間複雜度可行性證明

Bug分析

HW1

  • 由於對addRelation的JML規格機械實現,導致其邏輯有所遺漏在這裏插入圖片描述
  • 此處id1 != id2是bug所在,JML中signals的子條件並沒有表述完全,而是在大分分支exceptional_behavior下說明的,這一點會導致同在一個連通塊下添加Relation的時候會拋出異常在這裏插入圖片描述

HW2

  • 在實現qnr的時候,嘗試用java自帶的TreeSet進行查詢rank
  • 但其實name可以有相同的,而TreeSet是不可重複記集合,導致出錯
  • 修復方法爲使用可從重複元素的記集合,或者直接使用遍歷的方法實現qnr在這裏插入圖片描述

HW3

  • 在最短路的實現上,常數需要進行嚴格的控制,使用HashMap和HashSet進行記錄大大增加了常數在這裏插入圖片描述
  • 最好的方法是採用數組的方法,因爲調用最短路算法時,people的大小已經確定了,所以不需要使用動態的容器減少擴容和hash計算的時間
  • 其次,由於只需要找一個點的最短路徑,可以在點出隊時候直接剪枝,大大節省時間,因爲這時候根據Dijkstra的原理,目標都點的最短路已經不會改變了在這裏插入圖片描述

心得體會

  • 個人感覺JML理論支撐較爲成熟,但實際使用上並沒有很輕鬆
  • 首先JML規格的書寫並不能替代原來寫代碼時的邏輯思考,而且基於規格的代碼保證是正確的,但JML規格的邏輯正確性仍然得不到保證,所做的只是將代碼實現的正確性保證轉移到了JML規格的書寫上
  • 其次,JML的工具鏈使用實在是難以接受,花費了大量時間在JML相關工具鏈的使用上,而正確性並沒有很好地保證,到頭來還是需要自動評測機地幫助
  • 但另一方面,JML的相關工具還是可以使用的,作爲輔助黑盒測試的工具,使用EvoSuite和JMLUnitNG進行邊界數據的測試和覆蓋性測試
  • 最後,這個單元得到一個血的教訓,沒有親自做好充分的測試都是不可靠的,總會被感覺所矇蔽雙眼!一切都應該以數據說話!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章