kVerifier について

■■ kVerifier について ■■

kVerifier は C/C++ プログラムのテスト・検証ライブラリです。

  1. 単純な文法の action scripts にを使ってプログラムの動作を記述しておき、
  2. :アプリケーション・プログラムにkVerifier library をリンクし

アプリケーションをシミュレーション実行することで、プログラム動作が予定どおりであることをコンピュータにテスト・確認させます。テストはユニット・テストだけではなく全体テストが可能です。実用的に coverage 100% テストを記述できます。

さらに kVefier は時間進行を管理できます。action scripts にも時間を指定できます。すなわち、時間に依存して動作する組み込むプログラムでも、シミュレーション実行し、テスト・検証できます。

■kVerifier を使う理由

kVerifier はプログラム開発を楽にします。単純なプログラムのデバッグ作業をコンピュータに代行させることを可能にします。

メモリコストの低下に伴って、プログラムの規模が大きくなって来ています。一万行を超えるプログラムは小さいほうでしょう。プログラムの規模がおおきくなると、デバッグが大変になります。コード開発工数をデバッグ工数が上回ってきます。一方でデバッグ作業は単純作業です。工数の高い知的なプログラマーでなく、kVerifier にデバッグをさせるべきです。

プログラム規模の増大に伴ってプログラム仕様書も膨大になってきます。一千ページを超えるプログラム仕様書は珍しくもありません。膨大な詩陽子は作成すること、全部を性格に読み取ることが難しくなります。kVerifier を使って実行可能な machine readable な仕様を作成すべきです。

■kVerifier の効果

kVerifier はデバッグ作業をコンピュータに代行可能にします。デバッグ作業自体はコンピュータに代行させるべき非人間的な作業です。プログラムの仕様検討やコード開発のように知的な作業ではありません。しかも、この単純作業はプログラムの修正の度に行なわねばなりません。プログラムの動作を action scripts として記述しておくことで、

ワンチップの組み込みプログラムは C で記述されることが普通になりました。kVerifier を使って組み込みプログラムをシミュレーション実行、動作検証することは ICE デバッグ機能の多くを kVerifier を使って行なえることになります。

回路記述も SystemC による記述に移ってきています。SystemC 記述は C++ プログラム記述です。kVerifier を使ってシミュレーション実行、テスト・検証できます。回路記述とマイコン・プログラムの組み合わせ動作も対象にできます。

kVeriifer は詳細仕様の記述も対象とします。action scripts は動作具体例の羅列であり、仕様喜寿にも使えるからです。仕様記述のためのスレッド・クラスを用意しているからです。強力な STL を使ってコンカレントに動作する仕様プログラム・コードを作り仕様とします。動作具体例を羅列しておき、仕様とします。これがあれば、同じ動作をする組み込み C プログラムや SystemC 回路が仕様と同じであることをコンピュータに確認させられるようになります。詳細仕様記述をテスト・データにも使えるようになります。

テストもソフト資産の一部です。でも紙にテスト結果を記録しただけでは、テストしたことがあることを示すだけの価値しかありません。。kVerifie r を使えば、テストを再利用可能なソフト資産として蓄積していくことを可能にします。

■ action scripts とテストのための追加コード

action scripts とはプログラム動作具体例を、プログラム入力シーケンスと、その入力を応答して出力される予定値を並べたものです。動作具体例を羅列することで、プログラム・コード・アルゴリズムとは異なった側面からプログラム動作を記述します。

実際のプログラム例で action scripts と、それに従ったチェックを可能にする kVeriifer 追加コードを示します。

        --- action stripts 例 ----------------
      >type v\testFactorial.vrf
        +1c testFunction __set      3
        +0  inResultStt  __compare  6

        +1c testFunction __set      1
        +0  inResultStt  __compare  1

        +1c testFunction __set      0
        +0  inResultStt  __compare  1

        +0 __end

これは、数学の n! を計算する関数をテストする action scripts です。3! = 3*2*1 = 6 を

        +1c testFunction __set      3
        +0  inResultStt  __compare  6

の二行で確認しています。ここで 1c は 1 cycle 目の意味です。組み込みなどの時間に依存して動作する action scripts を記述するときは 1mS / 1uS などの時間単位を使った記述になります。

"testFunction __set 3" は factorial(.) 関数を動作させる testFunction(int) を引数 3 で呼び出します。その factorial(.) の戻り値が inResultStt に残っているはすなので、プログラムの動作結果は 6 になっているはずです。それを "inResultStt __compare 6" で記述します。

実際の factorial(.) プログラムと、それを上の v\testFactorial.vrf でテストするためのコードを下に示します。ファクトリアル関数を計算する factorial(.) の動作は明白なので、テストのための追加コードに的を絞って説明します。

//03.05.6 factorial calcuration test program
#include 

int factorial(int inAg)
{
    if ( inAg >= 1){
        return inAg * factorial(inAg - 1);
    }else{
        return 1;
    }
}


#ifdef DfVrfy
//--------- additional test code begin ------------
#include 
using namespace std;
using namespace kk;

int inResultStt;
void testFunction(int inAg)
{
    inResultStt = factorial(inAg);
}

static class ClTestVctD : public ClVrfyLibTstVct {
  public:
  protected:
    virtual void doAtInitial( const std::string& crStrAg);
} clTestVctDStt;

void ClTestVctD::doAtInitial( kc string& crStrAg)
{
    if ( IsSameNocase(crStrAg,"test.vrf") 
      || IsSameNocase(crStrAg,"v\testFactorial.vrf") 
    ){
        ClTestVct::doAtInitial(crStrAg);  // open crStrAg file
        kcout << "Now verifying input file is " << crStrAg << " in main.cpp" << endl;

        RgstVerified(this, tfNewVerified(inResultStt, "inResultStt") );
        RgstVerified(this, tfNewVfFnctn(testFunction, "testFunction") );
    }
}

//--------- additional test code end ------------
#endif //DfVrfy

"v\testFactorial.vrf" は action scripts ですが、それは単なるテキストにすぎません。factorial(.) 関数の動作と action scripts を協調して動作させる仕組みが必要になります。それを "additional test code begin --- end" の間にあるコードが行ないます。

"VrfyLib.h" は、ライブラリをテストするためのインターフェース・クラス ClVrfyLibTstVct を使用可能にします。ClVrfyLibTstVct を継承したクラス ClTestVctD をアプリケーションにあわせて作ります。仮想関数 doAtInitial(.) を継承し、アプリケーションにあわせたテストのためのコードを記述します。

ClTestVctD のインスタンス clTestVctDStt をスタティックに生成します。これにより、main(.) 関数を呼び出す前にテストのための kVerifier クラスが生成されていることを保証します。doAtInitial(.) を main(.) 開始直後の初期化の最中に kVerifeir が呼び出すことを保証します。ユーザーが別の個所から doAtInitial(.) を明示的に呼び出す必要はありません。

ClTestVctD::doAtInitial(.) の中の
                ・
        RgstVerified(this, tfNewVerified(inResultStt, "inResultStt") );
                ・

が inResultStt 変数のアドレスと型を kVeriifer に kVerifier Lib に登録します。tfNewVerified(.) がアドレスと変数型を保持するベリファイ変数クラスを作ります。tfNewVerified(.) は template 関数であり、変数の型に合わせたベリファイ変数クラスを生成します。vrf\VrfyVar.h に、この template 関数郡が記述されています。C++ プログラムの力があるひとならば、これを見れば kVerifier が何をどんなふうにしているか読み取れます。

testFunction(.) は factorial(.) をテストするバッファ関数です。factorial(.) 呼び出し、その戻り値をスタティック変数 inResultStt にセーブします。kVerifier がテストできるのはアドレスと型が定まっている C++ 変数です。factorial(.) の戻り値は temporary な値でしかなく、left side value になれません。アドレスが定められません。factorial(.) の戻り値を inResultStt に代入してやることで、factorial(.) の動作を確認可能にします。

        RgstVerified(this, tfNewVfFnctn(testFunction, "testFunction") );

でテストするバッファ関数 testFunction(.) を kVerifier に登録しています。C++ では関数をアドレスと型をもった変数として扱えるます。tfNewVfFnctn(.) template 関数を使ってベリファイ変数クラスを生成し、RgstVerified(.) で kVerifier Library に登録できます。

tfNewVerified(.) と tfNewVfFnctn(.) とでは template 関数名が変わっています。できたら同じ関数名を使いたいのですが、現状の C++ template 関数の特別バージョンでは compiler がこれらの識別をできません。基本変数、関数、クラス・コンテナ変数などにあわせてユーザーが明示的に区別して tfNew...(.) を選択記述する必要があります。

下のような tfNew(.) を用意してあります。これら選択使用することで、クラス変数や vector などの STL コンテナ変数をベリファイ変数として扱えるようにします。

◇ class ClBaseVerified
    △
   TcBaseVerified
    △
    ├─ TcVerified* tfNewVerified(T& rTAg, const std::string& crStrAg)
    ├─ TcVerified* tfNewVerified(T& rTAg, const std::string& crStrAg, double dbPrecisionRateAg)
    ├─ TcVrfyCntnr* tfNewVrfyCntnr(T& rTAg, const std::string& crStrAg)
    ├─ TcVrfyCntnr* tfNewVrfyCntnr(T& rTAg, const std::string& crStrAg)
    ├─ TcVrfyCntnrFp* tfNewVrfyCntnr(T& rTAg, const std::string& crStrAg, double dbPrecisionAg , long lgIosFlag=std::ios::scientific )
    ├─ TcRgstFnctn* tfNewVrfyFnctn(R(*pfAg)(T), const std::string& crStrAg)
    ├─ TcRgstFnctnVd* tfNewVrfyFnctnSpl(R(*pfAg)(void), const std::string& crStrAg
    ├─ TcRgstFnctnStr* tfNewVrfyFnctnSpl(R(*pfAg)(const std::string&)
    ├─ TcRgstFnctnStr* tfNewVrfyFncntSpl(R(*pfAg)(std::string&)
    ├─ TcClRgstFnctn* tfNewVrfyClFnctn(Ty* pTy, R(Ty::*pfAg)(T), const std::string& crStrAg)
    ├─ TcClRgstFnctnVd* tfNewVrfyClFnctnSpl(Ty* pTyAg, R(Ty::*pfAg)(void), const std::string& crStrAg)
    ├─ TcClRgstFnctnStr* tfNewVrfyClFnctnSpl(Ty* pTyAg, R(Ty::*pfAg)(const std::string&), const std::string& crStrAg)

■ main loop 関数について

factorial(.) 関数のコード例には main 関数がありません。でも、このプログラム・コードだけで実際に VC++ の IDE で動作します。その理由は vrfyMDd.lib の中に下のような main(.) 関数が用意してあるからです。

#include >verifier.h<
using namespace kk;
int main()
{
#ifdef DfVrfy
    try{
        return ClVrfySglt::GetStt()->Main(argc, argv);
    }catch(ClRglError* pClRglError){
        strErrorThrowedStt = pClRglError->m_strCst;
        cout << "Execption Error: " << pClRglError->m_strCst << endl;
        delete pClRglError;
        return 1;
    }
#endif  //DfVrfy
    return 0;
}

ClVrfySglt::GetStt()->Main(argc, argv) の中で ClTestVctD::doAtInitial(.) を呼び出し、action scripts ファイルの各行を読み出します。action scripts の引数文字列をアプリケーションの変数型の値に変換して、set し compare します。

■まとめ

kVerifier ではプログラムの動作具体例を action scripts として、入出力変数の変化シーケンスを text file に記述しておきます。ClVrfyLibTstVct を継承したくらすを作成し、doAtInitial(.) の中身をアプリケーションにあわせて記述することで、文字列の塊に過ぎない action scripts をアプリケーションプログラムと協調して動作させることを可能にします。