2013年3月29日金曜日

iutest v1.4.0 をリリースしました

iutest v1.4.0 をリリースしました。

今回は、古いコンパイラへの対応強化と C++11 の機能の取り込みを行なっています。
また、いくつか機能追加もしております。

C++11 の機能の取り込み
initializer_list

::iutest::ValuesIn を初期化リストに対応させました。
このような記述ができるようになります。
IUTEST_INSTANTIATE_TEST_CASE_P(My6, ParamTest
    , ::iutest::ValuesIn({ 3, 2, 1, 0 }));

また、実験的ではありますが、
tr1/iutest_param_direct_tests.hpp
を include することで下記のような記述も可能になります。
IUTEST_INSTANTIATE_TEST_CASE_P(My7, ParamTest
    , { 3, 2, 1, 0 });

::iutest::Values で事足りていると思うので、あまり使いどころはないかもしれません。
IUTEST_INSTANTIATE_TEST_CASE_P(My3, ParamTest
    , ::iutest::Values(3, 2, 1, 0));

extern template

ライブラリビルド向けに extern template を使ってみました。
個人的な勉強のために使ってみた程度です。

random (distribution)

特定範囲の乱数生成に uniform_int_distribution を使うようにしました。
また、::iutest::detail::iuRandom クラスを distribution の要求仕様に対応させました。

テスト関数から random_engine() 関数を呼ぶことで iuRandom を取得可能です。
IUTEST(RandomEngineTest, Test)
{
    IUTEST_ASSERT_LE(
        ::std::uniform_int_distribution<unsigned int>(0, 1)(random_engine()), 1);
}

追加機能
::iutest::RandomValues
パラメータの型が取りうる値をランダムにパラメータとして設定します。
class RandomValuesTest1 : public ::iutest::TestWithParam<int> {};
class RandomValuesTest2 : public ::iutest::TestWithParam<char> {};
class RandomValuesTest3 : public ::iutest::TestWithParam<float> {};

IUTEST_P(RandomValuesTest1, Test)
{
    int v = GetParam();
    IUTEST_SUCCEED() << v;
}
IUTEST_P(RandomValuesTest2, Test)
{
    char v = GetParam();
    IUTEST_SUCCEED() << v;
}
IUTEST_P(RandomValuesTest3, Test)
{
    float v = GetParam();
    IUTEST_ASSERT_GE(1, v);
    IUTEST_ASSERT_LE(0, v);
    IUTEST_SUCCEED() << v;
}

IUTEST_INSTANTIATE_TEST_CASE_P(A, RandomValuesTest1, ::iutest::RandomValues(5));
IUTEST_INSTANTIATE_TEST_CASE_P(A, RandomValuesTest2, ::iutest::RandomValues(5));
IUTEST_INSTANTIATE_TEST_CASE_P(A, RandomValuesTest3, ::iutest::RandomValues(5));


::iutest::ValuesGen
::iutest::RandomValues を作るにあたって汎用的に使えそうだったので、追加しました。
::std::generate のように指定数分、関数オブジェクトを使ってパラメータ設定をします。

struct RandomVMExceptMoneyGenerator
{
    ::iutest::detail::iuRandom engine;
    int operator ()(void)
    {
        for(;;)
        {
            int n =  engine() % 1000;
            if( n != 10 && n != 50 && n != 100 && n != 500 ) return n;
        }
    }
};

IUTEST_INSTANTIATE_TEST_CASE_P(Random03, ValuesGenTest
    , ::iutest::ValuesGen(5, RandomVMExceptMoneyGenerator()));


::iutest::RandomValues もそうですが、::iutest::ValuesGen がなくても同様のパラメータテストは作成できます。
上のテストを書き直すと、このようになります。

struct RandomVMExceptMoneyGenerator
{
    ::iutest::detail::iuRandom engine;
    int operator ()(void)
    {
        for(;;)
        {
            int n =  engine() % 1000;
            if( n != 10 && n != 50 && n != 100 && n != 500 ) return n;
        }
    }
};

::std::vector<int> RandomVMExceptMoneyParams(int n)
{
    ::std::vector<int> v(n);
    ::std::generate(v.begin(), v.end(), RandomVMExceptMoneyGenerator());
    return v;
}

IUTEST_INSTANTIATE_TEST_CASE_P(Random03, ValuesGenTest
    , ::iutest::ValuesIn(RandomVMExceptMoneyParams(5)));


複数の型付けテストに考慮
複数の型を型付けテストでテストしたい場合を考え、修正を加えました。
template<typename T>
class MultiTypedTest : public ::iutest::Test
{
};
typedef ::iutest::Types< ::iutest::Types<int, float>, ::iutest::Types<int, double> > MultiTypedTestTypes;

IUTEST_TYPED_TEST_CASE(MultiTypedTest, MultiTypedTestTypes);

IUTEST_TYPED_TEST(MultiTypedTest, Get)
{
    typedef typename TypeParam:: template get<0>::type  Type1; // 型を取得する get を追加
    typedef typename TypeParam:: template get<1>::type  Type2; // template 引数に何番目の型を指定します
    
    ::iutest::StaticAssertTypeEq< Type1, int >();
    IUTEST_SUCCEED() << ::iutest::detail::GetTypeName< Type1 >();
    IUTEST_SUCCEED() << ::iutest::detail::GetTypeName< Type2 >();
}

複数の型を持った ::iutest::Types をパラメータとして TYPED_TEST を作成します。
各型を ::iutest::Types::get で取得できるようにし、複数の型に対するテストを記述しやすくしました。

次バージョンの予定
実行順序の指定に対応予定です。
あとは、そろそろスレッド周りを見ていこうかなー…という感じです。

2013年3月25日月曜日

[c++] initializer_list: 初期化リストを () で括ったらエラー

c++11 では vector などの初期化が簡単に書けるようになりました。

こちらの機能を利用して iutest のコードを書いていたのですが、
これダメなのか~と思うことがあったのでブログに残しておきます。

こちらがそのコード。
#include <vector>
 
template<typename T>
void f(::std::initializer_list<T>)
{
}
 
int main(int, char**)
{
    f({1,2,3}); // OK
    //f(({1,2,3})); // NG
    ::std::vector<int> a = {1,2,3}; // OK
    //::std::vector<int> b = ({1,2,3}); // NG
    return 0;
}

() でくくったらダメなんですね。

やりたかったこととしては、マクロ引数に初期化リストを渡したかっただけなので、
可変長引数を使うようにしました。

template<typename T>
void f(::std::initializer_list<T>) {}
void f(int) {}

#define HOGE(x) f(x)

void g()
{
    HOGE(0);
    HOGE(({1, 2, 3}));
}



template<typename T>
void f(::std::initializer_list<T>) {}
void f(int) {}

#define HOGE(...) f(__VA_ARGS__)

void g()
{
    HOGE(0);
    HOGE({1, 2, 3});
}


2013年3月23日土曜日

[Jenkins] 最近頻繁に master が落ちると思ったらプラグインのバグだったもよう…

少し前から(何時頃からかは忘れたが) Jenkins のマスターが落ちることが度々発生していました。

ログも出さずに落ちるもんだから、手探り状態で原因を探っておりました。
不要なプラグインを消したり、マシンの構成を変えたり、Java をアップデートしたり、Windows Update したり、Jenkins おじさんにお祈りしたり…

もうわけわからん…

そんなこんなで時が経ち…

ふと、いつものメンテナンス業務をしていると、プラグインの更新があったのでいつもの通り更新履歴を読んでましたら…
Timestamper 1.5.3
•Workaround a bug in Jenkins which causes a VM crash (JENKINS-16528)

あれ、もしかして?
JENKINS-16528: Jenkins master node crashes often when viewing console logs of builds on slaves

うわぁぁぁ…
とりあえず、アップグレードしておきました。
(※直ったかどうかは未確認)

プラグイン更新は慎重にしたほうが良いかも…
git plugin の件もあったし、(Jenkins git plugin 爆死事例集
プラグイン更新はもっと慎重に、そして何かあったら戻すように心がけます。

2013年3月19日火曜日

[AutoIt] スクリプトが Windows 8 で動作しない場合の対処

AutoIt のスクリプトを Windows 7 や Xp などで実行した場合には正常動作するのに、
Windows 8 の場合にうまくいかなかった場合の対処方法をメモっておきます。

参考にしたのは、こちら
AutoIT and windows 8 - MSFN Forum

UAC を無効にしたら動くらしいのですが、
どうやらコントロールパネルから UAC を「通知しない」にしてもダメなようです。

UAC を無効にするには、
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Policies/System/EnableLUA
の値を 0 に設定するそうです。

こちらの値を変更すると再起動が要求されます。
再起動後、ちゃんと動くようになりました。

2013年3月15日金曜日

autest 1.0.0 β をリリースしました

AutoIt 用テスティングフレームワークの autest v1.0.0β をリリースしました。

テストの自動収集、実行テストの選択、xml ファイルの出力などが出来るようになり、大分それっぽくなってきました。

もう少しコードを整理して正式リリースをする予定です。

2013年3月12日火曜日

Jenkins + Softalk で容疑者を音声出力させてみた

Groovy post build plugin を使って Softalk から音声出力させてみました。
コードはこちら。
import hudson.util.RemotingDiagnostics

def res = manager.build.getResult();
if( res.isWorseThan(hudson.model.Result.SUCCESS) ) {
    def users = manager.build.getCulprits();
    if( users != null ) {
        def command = "softalkw.exe /W:";
        def find = false;
        users.each {
            command += "${it.getDisplayName()} ";
            find = true;
        }
        if( find ) {
            command += " アウトーーー!";
            manager.listener.logger.println(command);

            def computer = manager.build.getBuiltOn().toComputer();
            def channel = computer.getChannel();
            
            def script = """
                '${command}'.execute();
            """

            RemotingDiagnostics.executeGroovy( script, channel );
        }
    }
}



manager.build.getCulprits() で容疑者のリストが取得できます。
リストからアカウント名を取得し、softalk に読み上げさせたい文字列を作成しています。
アカウント名のままだとアルファベットを読み上げるだけになってしまうので、事前に
softalkw /P:ずみっくす,srz-zumix,True
のようにして単語を登録しておいてください。

メール通知だけの環境で満足してない方は試してみるのもありかも。

2013年3月6日水曜日

[Jenkins][Parameterized Trigger Plugin] 上流ビルドと同じスレーブ(ノード)で実行する

ビルドを実行したマシンでしかテストができないとか、上流の結果をスレーブマシン上に残して下流で利用するとか、
(あんまりいい例が思い浮かばないが…)上流ビルドと同じスレーブマシンで下流ビルドを実行したい場合があると思います。

実際に会社で Jenkins を使っていてそういうことがありました。
(今は2つのプロジェクトを1つにまとめてしまいましたが…)

最近、プラグインの更新履歴を見ていたら、
Parameterized Trigger Plugin の更新履歴(2.17 (Feb 26, 2013))に
Define impllicit parameter: "build them on the same node" (issue #16334)
という履歴がありました。



思った通り、「build on the same node」というパラメーターか追加されてました。
これで、上流と同じノードで実行できそうです。

2013年3月5日火曜日

[Jenkins] ビルド番号の不整合を検出する

Jenkins がブルースクリーンなどで正常に終了しなかった場合、ビルド番号がリセットされてしまうことがあります。
(最新のバージョンではそんなことはないかもしれませんが)
その状態で古い履歴を削除する設定をしていると、
ビルド番号の大きい古いビルドが削除されず、ビルド番号の小さな新しいビルドが削除されてしまいます。

これが意外と気づきにくいんですよね。

というわけで自動化しましょう!
不整合の検出
@echo off

pushd "%~dp0"

if "x%~1" == "x" (
    call :target_jenkins_home
    if errorlevel 1 goto error
) else (
    call :target_command_line %*
    if errorlevel 1 goto error
)

SET TARGET_PATH
if errorlevel 1 goto error

SET ERRORCODE=0
for /D %%d in ( "%TARGET_PATH%\jobs\*" ) do (
    @echo %%~nd
    if exist "%%d\nextBuildNumber" (
        for /f %%i in ( %%d\nextBuildNumber ) do (
            call :checkbuildnumber %%d %%i
            if errorlevel 1 (
                SET ERRORCODE=1
            )
        )
    )
)

if "%ERRORCODE%" == "1" goto error
goto end

:checkbuildnumber
SET NEXT=%~2
rem @echo next build number %NEXT%

SET MAXNUM=0
setlocal enabledelayedexpansion
for /D %%d in ( "%~1\builds\*" ) do (
    call :getbuildlognumber %%d
    SET CURNUM=!errorlevel!
    if !CURNUM! gtr !MAXNUM! (
        SET MAXNUM=!CURNUM!
    )
)
rem @echo !MAXNUM!
if %NEXT% lss !MAXNUM! (
    @echo "Warning: next build number [%NEXT%] is smaller than the number of the log [!MAXNUM!]."
    exit /b 1
)
endlocal
exit /b 0

:getbuildlognumber
if not exist "%~1\build.xml" exit /b 0
for /f "tokens=1,2,3,4* delims=<>" %%a in ( %~1\build.xml ) do (
    if "%%b" == "number" (
        exit /b %%c
    )
)
exit /b 0

:target_command_line
if not exist "%~1" (
    @echo %~1 は存在しません。
    exit /b 1
)

SET TARGET_PATH=%~1
exit /b 0

:target_jenkins_home
SET JENKINS_HOME >NUL
if errorlevle 1 (
    @echo JENKINS_HOME が設定されていません。
    exit /b 1
)

SET TARGET_PATH=%JENKINS_HOME%
exit /b 0

:error
popd
@echo failed.
pause
exit /b 1

:end
popd
exit /b 0
bat ファイルでございます。
master ノードで実行する必要があります。
第一引数に ${JENKINS_HOME} のパスを指定します。省略した場合、環境変数のパスをそのまま使います。
こんなんでも定期的に実行するようにしたら、効果はきっとあるでしょう。

もう少しスマートに検出する
Groovy post build plugin を使って検出させてみました。
import hudson.model.AbstractProject

def jenkins = hudson.model.Hudson.instance

def projects = jenkins.getAllItems(AbstractProject.class)
projects.each { project ->
    def lastBuild = project.getLastBuild()
    if( lastBuild != null )
    {
        def lastNumber = lastBuild.getNumber()
        def nextNumber = project.getNextBuildNumber()
        if( nextNumber < lastNumber )
        {
            manager.listener.logger.println(project.getName())
            manager.listener.logger.println("Warning: next build number [${nextNumber}] is smaller than the number of the log [${lastNumber}].")
            manager.buildUnstable()
        }
    }
}
たったこれだけで出来ました~(^^
修復
ビルド番号の修正は Jenkins が"停止"しているときにする必要があります(たぶん)。 不整合の検出で使用したバッチファイルを少し変更して、不整合が見つかったら nextBuildNumber を修正します。
@echo off

pushd "%~dp0"

if "x%~1" == "x" (
    call :target_jenkins_home
    if errorlevel 1 goto error
) else (
    call :target_command_line %*
    if errorlevel 1 goto error
)

SET TARGET_PATH
if errorlevel 1 goto error

for /D %%d in ( "%TARGET_PATH%\jobs\*" ) do (
    @echo %%~nd
    if exist "%%d\nextBuildNumber" (
        for /f %%i in ( %%d\nextBuildNumber ) do (
            call :repairbuildnumber %%d %%i
        )
    )
)

goto end

:repairbuildnumber
SET NEXT=%~2
rem @echo next build number %NEXT%

SET MAXNUM=0
setlocal enabledelayedexpansion
for /D %%d in ( "%~1\builds\*" ) do (
    call :getbuildlognumber %%d
    SET CURNUM=!errorlevel!
    if !CURNUM! gtr !MAXNUM! (
        SET MAXNUM=!CURNUM!
    )
)
rem @echo !MAXNUM!
if %NEXT% lss !MAXNUM! (
    @echo "Warning: next build number [%NEXT%] is smaller than the number of the log [!MAXNUM!]."
    SET /a NEWNUM=!MAXNUM!+1
    @echo new next build number !NEWNUM!
    @echo !NEWNUM!| sed 's/\n//g'> "%~1\nextBuildNumber"
)
endlocal
exit /b 0

:getbuildlognumber
if not exist "%~1\build.xml" exit /b 0
for /f "tokens=1,2,3,4* delims=<>" %%a in ( %~1\build.xml ) do (
    if "%%b" == "number" (
        exit /b %%c
    )
)
exit /b 0

:target_command_line
if not exist "%~1" (
    @echo %~1 は存在しません。
    exit /b 1
)

SET TARGET_PATH=%~1
exit /b 0

:target_jenkins_home
SET JENKINS_HOME >NUL
if errorlevle 1 (
    @echo JENKINS_HOME が設定されていません。
    exit /b 1
)

SET TARGET_PATH=%JENKINS_HOME%
exit /b 0

:error
popd
@echo failed.
pause
exit /b 1


:end
popd
pause
exit /b 0
※上記バッチファイルの使用は自己責任でお願いします。

2013年3月1日金曜日

iutest_c v1.1.0 をリリースしました

iutest_c v1.1.0 をリリースしました。

ライブラリビルド対応とテストフィクスチャにユーザーデータを渡せるようになってます。
次は値のパラメータ化テストへの対応を予定していますが・・・できるのだろうか・・・