読者です 読者をやめる 読者になる 読者になる

テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(macro-of-inline report) rubyテスト改善

昨日, --fake-includeというオプションを追加したり, 色々と修正を行った結果, rubyソースツリーの変換とコンパイルがかなり改善しました.

まず, 成功ファイル数は, 82%になりました. 以前は68%だったので, 大幅に上がりました. Rubyは大体300ちょいのCコードがあるのですが, 失敗はたった64個です.

コンパイルについては, 今まで止まっていたcomplex.cがコンパイル出来るようになりました.

このうちどちらを上げるのが単調かというと, 成功ファイル数の方です. pycparserがこけてるのでしょうから, 変換に失敗したものを個別に変換してみて, 原因を突き止めればいいです. その修正も大抵は, fake includeへの単純な追加で終わるでしょう.

骨なのは後者です. 変換は正しいけどコンパイルに失敗しているというケースは, 変換アルゴリズムに誤りがあることを意味しています. 現在把握しているバグは1つですが, この修正は難しいので, 出現頻度が低いとみて保留しています(他のバグフィックスとの兼ね合いで, やってはいけない変更があるため今直すべきではないと判断). こちらはそもそもバグを突き止めるのが難しく, 突き止めて小さなケースで再現出来たとしても直すことは大抵骨です. 一般則として, ほどほどに動いてるものの中にあるバグは殺しにくいです. ソフトウェアを作るというのは自由度を削るということなので, すでにそのバグを殺せる自由度を失ってる可能性もあります.

とはいえ, あと数個あるだろうバグを潰せば, 最後はパタパタと行きそうもします. あと少し粘りますか.

ちなみに, fake includeというのは, プリプロセッサの-includeオプションで一時的に型情報のヒントを上げて, 出力されたコードからはその偽の情報を全部取り除くということです. 例えば typedef int __int128; などと書いています.

コードは以下のような感じです. AST->AST変換の部分を, if fake_includeでサンドイッチしています. 前のものでコードを注入して, 後のものでASTレベルで剥ぎとっています(ast_delete).

                fake_include = cfg.t.fake_include

                if fake_include:
                        fn = "/tmp/%s.c" % utils.randstr(16)
                        with open(fn, "w") as fp:
                                fp.write(self.txt)

                        # TODO Ugly. Roan pattern
                        try:
                                cpp_args = ['-E', r'-include%s' % fake_include]
                                cpped_txt = pycparser.preprocess_file(fn, cpp_path='gcc', cpp_args=cpp_args)
                        except Exception as e:
                                sys.stderr.write(e.message)
                                sys.exit(1)
                        finally:
                                os.remove(fn)
                else:
                        cpped_txt = self.txt

                ast = AST(ext_pycparser.ast_of(cpped_txt)).run().returnAST()

                if fake_include:
                        with open(fake_include) as fp:
                                ast_b = ext_pycparser.ast_of(fp.read())
                        cppwrap.ast_delete(ast, ast_b)