參與專案的第一步當然是先看issues,那時剛好看到這個:
https://github.com/jserv/amacc/issues/13
看起來不算太難,改一下code 執行流程就好了
不過首先,這個project 有點難以測試,的確make check 會嘗試編譯所有tests/*.c 檔,並且用jit 和產生elf 的方式去執行,可是卻不會通知結果是不是正確的,nested for loop 的結果也會和其他檔案混在一起,直接下指令去測又很麻煩(要用qemu,指令好長…)
覺得測試這麼麻煩,連帶著改issue 也好麻煩的感覺,因此決定不想改了先來幫這個project 加上一些便於測試的功能
想到test 就想到python unittest,設計概念是:利用python subprocess 去呼叫amacc跟arm-linux-gnueabihf-gcc 分別編譯執行檔,執行後,直接比較兩者的output,看結果是否相同,如此就能驗證amacc 的行為(這有個問題是gcc 未必遵照C standard,真正正確的答案應該要看C standard 才對,不過我們假定大多數狀況gcc會遵照C standard)
想到test 就想到python unittest,設計概念是:利用python subprocess 去呼叫amacc跟arm-linux-gnueabihf-gcc 分別編譯執行檔,執行後,直接比較兩者的output,看結果是否相同,如此就能驗證amacc 的行為(這有個問題是gcc 未必遵照C standard,真正正確的答案應該要看C standard 才對,不過我們假定大多數狀況gcc會遵照C standard)
這裡遇到一個問題,在tests 裡面有許多的c files,因為對每個檔案要做的事情其實是一樣的,自己寫只會變成複製貼上,還可能貼錯內容;寫一個test 去跑所有檔案也不行,這樣等於是所有測試混在一起,test 沒過可能是任一個檔案有錯,根本看不出是誰錯了。
所幸這個解法也不困難,簡單google 就找到了:
概念是,本來的module 變成一個容器,裡面要自己寫的function 就不寫了:
class TestAmacc(unittest.TestCase):
pass
pass
另外寫一個共同的test generator,會傳回測試的function,我這個test function 裡面就是本來要對檔案做的事:amacc編譯、gcc 編譯、執行、比較結果,參數f為要執行的檔名:
def testGenerator(f):
def test(self):
# test function here
return test
def test(self):
# test function here
return test
最後是對每個檔案去呼叫這個generator,利用setattr 的方式,把這個function 督進先前宣告的容器當中,python 的彈性由此可見:
for dirpath, _, filenames in os.walk("tests"):
for f in filter(lambda name: namePattern in name, filenames):
testfile = os.path.abspath(os.path.join(dirpath, f))
test_func = testGenerator(testfile)
setattr(TestAmacc, 'test_%s' % (os.path.splitext(f)[0]), test_func)
unittest.main(argv=[sys.argv[0]])
for f in filter(lambda name: namePattern in name, filenames):
testfile = os.path.abspath(os.path.join(dirpath, f))
test_func = testGenerator(testfile)
setattr(TestAmacc, 'test_%s' % (os.path.splitext(f)[0]), test_func)
unittest.main(argv=[sys.argv[0]])
如此就能自動產生test case 了,如上所示,還可以幫檔名加上filter,用參數來指定要跑什麼測試,極度方便;這裡遇到一點卡關是,unittest 預設會吃主程式的argv作為要跑的test module 名稱,argv 加上參數的話會讓unittest 炸開,必須要避免unittest.main 吃到不是給它的argv 參數以,免它找不到測試module 直接回報出錯。
沒有留言:
張貼留言