今回は、「Pipeline as Code」に基づいた「動的テストの自動化」のためのスクリプトを考えて見たいと思います。第6回では、Jenkinsで「動的テストを自動化」するためのJobを設計しました。しかしながら、そのときに書いた内容は、言ってみれば一連の作業をバッチファイルとして登録してJenkins上で実行しただけのものでした。そこで、今回は「Pipeline as Code」を意識しながら、「動的テストの自動化」に必要な処理を「パイプライン」らしい記述方法に置き換えてみたいと思います。

 

「パイプライン」の新規作成

最初に、Jenkins上でパイプラインを新規作成します。Jenkinsのトップメニューから「新規ジョブ作成」をクリックします。ジョブの作成画面が開くので、図1のようにJobの名前を入力し、「パイプライン」を選択し、OKボタンを押します。

図1: Jobの新規作成

 

「パイプライン」の設定

パイプラインを新規作成後、自動でパイプラインの設定画面に切り替わりますので、続けてパイプラインの設定を行います。パイプラインの設定画面(図2)は、「フリースタイル・プロジェクトのビルド」と比べると、設定内容が少なく感じます。細かな設定内容はスクリプトとして書きなさいということなのでしょう。

図2: パイプラインの設定

「パイプライン」設定の「定義」は「Pipeline script」と「Pipeline script from SCM」の2種類から選べるようになっています。「Pipeline script」は、Jenkins上で直接Groovy形式でスクリプトを書くことを前提としています。図3は、Groovyで書かれたサンプルコードを表示したものです。

図3: Pipeline Script

一方の「Pipeline script from SCM」は、その名の通りGitやSubversionなどのSCMからスクリプト(Jenkinsfile)を取得し実行する方式となります(図4)。Jenkins上で実行するスクリプトのバージョン管理を考えると、必然的に「Pipeline script from SCM」を選択することになります。

図4: Pipeline Script from SCM

 

「Pipeline Script」で設計

続いて、パイプラインを使用した「動的テスト自動化」のCI環境を構築するための設計を行ってみたいと思います。前述の通り、パイプラインには「Pipeline script」と「Pipeline script from SCM」の2種類がありますが、今回は「Pipeline script」の方で設計を行ってみたいと思います。

第7回で触れたように、「動的テストの自動化」をCI化するのに必要なフェーズは大きく7つあります。

・コードインスペクション
・テストポイントの挿入
・ビルド
・ディプロイ
・テスト・テストレポート取得
・テストレポート解析
・テスト結果のアウトプット

これらのフェーズを「Pipiline Script」で言うところの「stage」として登録してあげると都合がよさそうです。まずは、サンプル的な記述として図5のように記述してみます。

図5: フェーズごとの記述サンプル

このスクリプトをJenkinsで「ビルド実行」してみると、図6のようなStage ViewがJenkins上に表示されます。

図6: Stage View表示

図5のスクリプトは、各stageごとにその名前を出力するだけの単純なもので必ず正常終了しますが、途中でエラーが発生すると、図7のように、エラーが発生したstageの色が変わりそこで処理が止まるため、どのフェーズでどのようなエラーが発生したかが分かりやすくなります。

図7: Stage Viewエラー表示

 

実行コマンド記述時の注意点

パイプラインで実行するスクリプトはGroovyで記述する必要がありますが、パイプラインで外部コマンドを実行するときのGroovyの記述は次のようになります。

node {
    stage('テストポイントの挿入')
        bat "DTCmd -newins ${DtProj} ${DtFileList}"
}

基本的に、「bat」コマンドを使用することで、それに続く文字列を外部コマンドとして実行することができます。

 

パイプラインScriptの記述例

弊社のデモ機の開発環境を対象としたとき、パイプラインのScriptは次のような記述となります。

def ResultFolder="${env.JENKINS_HOME}\\jobs\\${env.JOB_NAME}\\Builds\\${env.BUILD_NUMBER}\\DT10"
def DtRoot="${env.WORKSPACE}\\trunk\\AnalogBoxDemo1"
def DtProj="${ResultFolder}\\AnalogBoxDemo.rprj"
def DtTestTool="${env.WORKSPACE}\\trunk\\TestTool"
def DtFileList="${DtTestTool}\\filelist.txt"
def DtCsv="${DtTestTool}\\csv"
def TiHome='C:\\Program Files(x86)\\Texas Instruments'
def TiCcs="${TiHome}\\ccsv4"
def TiJava="${TiCcs}\\eclipse\\jre\\bin\\java"
def TiJar="${TiCcs}\\eclipse\\startup.jar"
def TiFlash="${TiHome}\\Stellaris\\LM Flash Programmer\\LMFlash"
def TiWorkSpace="${env.WORKSPACE}\\trunk"
def TiImage="${DtRoot}\\Debug\\AnalogBoxDemo.bin"
def TiFlashOpt='--debug-port=JTAG --interface=ICDI --speed=300000 --frequency=6'
def TiFlashReset='--hreset'
def TestCommand="${DtTestTool}\\bin\\TestCommand.exe COM4"

node {
    stage('コードインスペクション'){
        // 今回は処理しない
    }
    stage('テストポイントの挿入'){
        echo 'プロジェクトの新規作成'
        bat "mkdir ${ResultFolder}"
        bat "DTCmd -newproj ${DtProj} ${DtRoot} gpio4bit 0x00010"
        echo 'テストポイントの挿入'
        bat "DTCmd -newins ${DtProj} ${DtFileList}"
    }
    stage('ビルド'){
        bat "${TiJava} -jar ${TiJar} -data ${TiWorkSpace} -application com.ti.ccstudio.apps.projectBuild -ccs.workspace"
    }
    stage('ディプロイ'){
        bat "${TiFlash} ${TiFlashOpt} ${TiImage}"
    }
    stage('テスト・テストレポート取得'){
        bat 'DT10起動'
        bat "DTCmd -project ${DtProj}"
        echo 'データ収集開始'
        bat 'DTCmd -trace start'
        echo 'リセットスタート'
        bat "${TiFlash} ${TiFlashReset}"
        echo 'テスト自動実行'
        bat "${TestCommand} \"test up\""
        bat "${TestCommand} \"test down\""
        bat "${TestCommand} \"test rand\""
        bat "${TestCommand} \"test screen\""
        echo 'データ収集停止'
        bat 'DTCmd -trace stop'
    }
    stage('テストレポート解析'){
        bat 'DTCmd -analyze all'
    }
    stage('テスト結果のアウトプット'){
        bat "mkdir ${DtCsv}"
        bat "DTCmd -export coverage ${DtCsv}\\coverage.csv"
        bat "DTCmd -export exectime ${DtCsv}\\exectime.csv"
        bat "DTCmd -export period ${DtCsv}\\period.csv"
        echo 'DT10終了'
        bat 'DTCmd -exit save'
    }
}

最後に

同じようなバッチ処理でも、Jenkinsのパイプラインによってフェーズごとに分けて書くことができ、スクリプトの可読性もグッとよくなったと思います。
次回はいよいよ「動的テストの自動化を考える」シリーズの最終回となります。「動的テストの自動化を考える」シリーズの集大成として、より実践に近いかたちでパイプラインのスクリプトを組んでみて実際に動かしてみたいと思います。特に、GitのリポジトリからJenkinsfileを取得しつつ、一連の「動的テストの自動化」のための処理をCIで回すところまでを一気にやりたいと思いますのでご期待ください。

【ホワイトペーパー】CI環境における動的テストの適応

 - Jenkinsを使った動的テスト自動化の施策例 -