簡體   English   中英

單元測試編譯器

[英]Unit testing a compiler

什么被認為是對復雜單元(如編譯器)進行單元測試的最佳方法?

多年來我寫了一些編譯器和解釋器,我發現這種代碼很難以一種好的方式進行測試。

如果我們采用類似抽象語法樹生成的東西。 你會如何使用TDD測試?

小結構可能很容易測試。 例如:

string code = @"public class Foo {}";
AST ast = compiler.Parse(code);

因為那不會產生很多ast節點。

但是,如果我真的想測試編譯器可以為類似方法生成AST:

[TestMethod]
public void Can_parse_integer_instance_method_in_class ()
{
   string code = @"public class Foo {  public int method(){ return 0;}}";
   AST ast = compiler.Parse(code);

你會斷言什么? 手動定義代表給定代碼的AST,並斷言生成的AST符合手動定義的AST看起來非常簡潔,甚至可能容易出錯。

那么像這樣的TDD復雜場景的最佳策略是什么?

首先,如果您測試編譯器,則無法獲得足夠的測試! 用戶真的依賴於編譯器生成的輸出,就像它始終是黃金標准一樣,所以真的要注意質量。 所以,如果可以的話,測試你可以提出的每一個測試!

其次,使用所有可用的測試方法,並在適當的地方使用它們。 實際上,您可以在數學上證明某種轉變是正確的。 如果你能夠這樣做,你應該這樣做。

但是我看到的每個內部編譯器都涉及啟發式和許多優化的,手工制作的代碼在其內部; 因此,輔助證明方法通常不再適用。 在這里,測試到位,我的意思是很多!

在收集測試時,請考慮不同的情況:

  1. 正標准一致性:您的前端應該接受某些代碼模式,並且編譯器必須生成正確運行的程序。 此類別中的測試需要一個黃金參考編譯器或生成器,以生成測試程序的正確輸出; 或者它涉及手寫程序,其中包括對人類推理提供的價值進行檢查。
  2. 負面測試:每個編譯器都必須拒絕錯誤的代碼,如語法錯誤,類型不匹配等。 它必須產生某些類型的錯誤和警告消息。 我不知道有任何自動生成此類測試的方法。 所以這些也需要人為寫。
  3. 轉換測試:每當你在編譯器(中端)中進行花哨的優化時,你可能會有一些示例代碼來演示優化。 注意這樣一個模塊之前和之后的轉換,它們可能需要特殊的選項來編譯器或只有插入模塊的裸機編譯器。 測試一組合理的周圍模塊組合。 我通常在特定轉換之前和之后對中間表示進行回歸測試,通過與同事的密集推理來定義參考。 嘗試在轉換的兩側編寫代碼,即您想要轉換的代碼片段和不一定的稍有不同的代碼片段。

現在,這聽起來像是一項非常多的工作! 是的確如此,但有幫助:世界上有幾個用於(C-)編譯器的商業測試套件以及可能幫助您應用它們的專家。 這里有一小部分我知道的人:

首先,解析通常是編譯器項目的一個微不足道的部分。 根據我的經驗,它不會占用超過10%的時間(除非我們在談論C ++,但如果你正在設計它,你就不會在這里提問)所以你寧願不把大部分時間投入到解析器測試中。

盡管如此,TDD(或者你稱之為)還是在開發中端的過程中,你經常需要驗證,例如你剛剛添加的優化確實導致了預期的代碼轉換。 根據我的經驗,這樣的測試通常是通過給編譯器特制的測試程序和grepping輸出程序集來實現預期的模式(這個循環展開了四次嗎?我們設法避免內存寫入是這個函數嗎?等等)。 Grepping程序集不如分析結構化表示(S-exprs或XML),但它很便宜並且在大多數情況下都能正常工作。 盡管你的編譯器在增長,但是非常難以支持。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM