google testのテスト結果からhtmlを生成

この記事では、google testで出力されるテスト結果のレポートをhtmlにするための方法について説明します。


C++C言語のテストをするにはgoogle testの利用がデファクトとなっている状況かと思います。 テスト結果をhtml形式にすることで、テスト結果をより有効に活用できるようになります。


google testは、xml形式の出力ができます。その形式はjunitが用いる形式にほぼ準拠しています。ただし、google test(バージョン 1.5.0)の場合、一点だけ惜しいことにtestsuite要素にpackage属性が出力されないため、htmlに変換した際、きれいに出力されない問題があります。


この問題を解決するために次の方法をひとつ考えてみました。

必要なもの

  1. google test version 1.5.0
  2. ant 私はeclipseに内蔵されているantを用いています。
  3. build.xml -- この記事で説明しているファイル

build.xmlファイルの処理の流れについて説明します。

  1. google testのテスト結果をXML形式で出力します。ターゲットは、run_testです。
  2. xmlファイルのtestsuite要素にpackage属性を追加します。run_testターゲット内にあります。
  3. antのタスクのJUnitReportが利用しているjunit-frames.xslと上記のxmlファイルを元にxsltタスクを用いてhtmlファイルを出力します。ターゲットは、report_testです。
<project name="gtest_report_sample" default="report_test">
  <property name="reports" value="target"/>
  <property name="report_xml" value="${reports}/all-testsuites.xml"/>
  <property name="report_html" value="${reports}/index.html"/>

  <property name="executable" value="./Debug/gtest_sample"/>
  
  <target name="run_test"
      description="google testを実行">
    <delete dir="${reports}"/>
    <mkdir dir="${reports}"/>
    <exec executable="${executable}"> <!-- google testを実行 -->
      <arg value="--gtest_output=xml:${report_xml}"/>
    </exec>
    <!-- gtestのXML出力ではpackageが出力されないので、
	   強制的にpackage属性を追加。-->
    <replace file="${report_xml}" token="testsuite ">
      <replacevalue><![CDATA[testsuite package="default" ]]></replacevalue>
    </replace>
  </target>
  
  <target name="report_test" depends="run_test"
  	description="テストレポートをhtml形式に変換">
    <xslt style="${ant.home}/etc/junit-noframes.xsl"
        in="${report_xml}" out="${report_html}/">
    </xslt>
  </target>

</project>

テストレポートの画面イメージ


開発環境

参考までにですが、私が使っている開発環境は以下のとおりです。

  1. eclipse 3.6(helios)
  2. cdt(C/C++ Development Tools) eclipse プラグイン
  3. eclipseの内部に持っているant

UIコンポーネントをブリンクするコードビハインド

WPFにおいて、テキストやボタンなどのUIコンポーネントをブリンクさせるサンプルです。例は、300ミリ秒間にOpacity値を1.0から0に変化させることを3回繰り返すものです。

XAMLで記述するサンプルはネット上に色々あるのですが、ソースコードで記述した例が少ないですねぇ。

  DoubleAnimation blink = new DoubleAnimation();
  blink.From = 1.0;
  blink.To = 0.0;
  blink.AutoReverse = true;
  blink.RepeatBehavior = new RepeatBehavior(3);
  blink.Duration 
    = new Duration(TimeSpan.FromMilliseconds(300));

  Storyboard sb = new Storyboard();
  sb.Children.Add(blink);

  // viewにボタンとかテキストを指定してね。
  Storyboard.SetTarget(blink, view);
  Storyboard.SetTargetProperty(blink, 
    new PropertyPath("(UIElement.Opacity)"));
  
  sb.Begin();

上記のソースコードと同様な処理をXAMLを用いて記述した例です。ボタンをクリックしたときにブリンクします。

  <Button Content="Animation2" Height="23" Name="button3" Width="75">
    <Button.Triggers>
      <EventTrigger RoutedEvent="Button.Click">
        <BeginStoryboard>
          <Storyboard>
            <DoubleAnimation
               Storyboard.TargetProperty="Opacity"
               From="1" To="0" Duration="0:0:0.3"
               RepeatBehavior="3x" AutoReverse="true" />
           </Storyboard>
         </BeginStoryboard>
      </EventTrigger>
    </Button.Triggers>
  </Button>

コードビハインドで記述するのが簡単にならないかなぁ。

[C#][WPF] ObservableCollectionを非同期で利用する際

WPFを利用する開発は、Model View ViewModelパターンで設計することが多いですよね。
その際、データバインディングは外せないので、必然的にObservableCollectionをよく用いると思います。

けれども、ObservableCollectionってディスパッチャに対して安全ではないという大きな問題があります。例えば、独自のスレッドでModelの追加、削除して、その通知をViewModelのNotifyCollectionChangedデリゲートで処理します。その処理の中でViewモデルのDataContextにModelの参照を渡すと、例外が発生します。

この問題を解決する手法はいろいろありますが、個人的に一押しな手法はHave worker thread update ObservableCollection that is bound to a ListCollectionViewですね。

一押しの理由は、

  • シンプルなコード
  • 適切なDispatcher取得方法。初めてこのコードを見たとき、こんなうまい手法があるんだと感動しました。

どうぞお試し下さいね。

[java][c++] C++の共有ライブラリをJavaから呼び出す

C++を使って作成した共有ライブラリをJavaから簡単に利用する方法を紹介します。JNI(Java Native Interface)を使うよりはるかに簡単です。

用意するものは、jnaerator: http://code.google.com/p/jnaerator/ です。
jnaeratorは、JNA(Java Native Access): https://jna.dev.java.net/を利用して簡単に共有ライブラリへのアクセスを実現しています。

C言語についてのJavaから共有ライブラリへのアクセスの情報はいろいろとあったのですが、C++を使った共有ライブラリの情報が少なかったので、この記事を書きました。

C++のソースを作成.

ここは定番のHelloWorldでしょうか。

 $ vi HelloWorld.h
 #ifndef _HELLO_WORLD_H_
 #define _HELLO_WORLD_H_
 
 class HelloWorld {
 public:
     HelloWorld();
     void call();
 
 private:
     int called;
 };
 #endif
 
 $ vi HelloWorld.cc
 #include <iostream>
 #include "HelloWorld.h"
 
 HelloWorld::HelloWorld() {
     called = 0;
 }

 // このメソッドをJavaから呼び出します。 
 void HelloWorld::call() {
     printf("Hello World, called: %d\n", called++);
 }

コンパイル

 $ g++ -fPIC -c HelloWorld.cc

共有ライブラリを作成

 $ g++ -shared -Wl,-soname,libHelloWorld.so.1 -o libHelloWorld.so.1.0 HelloWorld.o
 $ ln -s libHelloWorld.so.1.0 libHelloWorld.so

jnaeratorを用いてjarを作成.

あらかじめ、jnaeratorをダウンロードしてください。
C++のコードを呼び出せるように-genCPlusPlusオプションを指定します。

 $ java -jar jnaerator-0.9.2.jar -genCPlusPlus HelloWorld.h
 .... HelloWorld.jarが生成される.

共有ライブラリは、生成されたjarの内部に含まれています。

javaのソースを作成.

共有ライブラリを呼び出しているようには見えないところがすばらしいとおもいます。

 $ vi CallHelloWorld.java
 import helloworld.HelloWorld;
 
 class CallHelloWorld {
    public static void main(String[] args) {
        HelloWorld hw = new HelloWorld();
        hw.call();
        hw.call();
    }
 }

コンパイル

 $ javac -cp HelloWorld.jar CallHelloWorld.java

実行.

無事,C++で作成した共有ライブラリが呼び出せました. 本当に便利ですねぇ

 $ java -cp HelloWorld.jar:. CallHelloWorld
 called: 0
 called: 1

いかがでしたでしょうか。

HashMap vs memcached vs jdbc 性能比較

ConcurrentHashMapとmemcachedjdbcの性能比較を行いました。memcachedのクライアントライブラリは、com.dangaとnet.spyの両方で実施しました。

計測は、それぞれ100万件のデータを生成したり、読み取った時間です。単位はmsecです。

対象 Create Bulk Create Read Bulk Read
ConcurrentHashMap 4,586 --- 859 ---
Memcache(com.danga) 422,290 216,359 592,190 90,960
Memcache(net.spy) 60,074 --- 425,191 49,515
jdbc(mysql) --- 88,974 654,895 11,554


計測時の注意点を列挙します。

  • com.dangaのmemcachedの「Bulk Create」は、複数スレッドでsetを実行し、最後にスレッドをshutdownしてすべてのスレッドが停止するまで待っています。
  • net.spymemcachedクライアントの「Create」は非同期APIです。同期系のsetのAPIは用意されていません。
  • jdbcの「Read」は、select * from test_users where id = ? で20000回計測し、それを5倍しました。
    id:sh2さんご指摘ありがとうございました。
  • jdbcの「Bulk Read」は'select * from test_users where ? < id and id <= ?'で10000件ずつ取得しました。

ConcurrentHashMapを1としてそれぞれどの程度遅いかの比較表です。

  • memcachedが速いとよく言われていますが、ローカルで動作するConcurrentHashMapと比較すると約495倍も遅いです。
  • net.spyとcom.dangaのライブラリの速度の違いは大きいですね。
  • 「Bulk Read」に関しては、jdbcの方がmemcachedより速いです。もしかするとmemcachedでは、シリアライズやデシリアライズのオーバヘッドが大きいのでしょうか?

対象 Create Bulk Create Read Bulk Read
ConcurrentHashMap 1 --- 1 ---
Memcache(com.danga) 92 47 689 105
Memcache(net.spy) 13 --- 495 10
jdbc(mysql) --- 19 762 13

計測用コード

public class User implements Serializable {
  String name;
  String uri;
  public User(String name){
    this.name = name; 
    this.uri = name + "@example.com";
  }
}

jdbc(mysql)では以下のテーブルを用いて計測しました。

create table if not exists test_users(
  id integer auto_increment not null,
  name varchar(128),
  uri varchar(128),
  index (name),
  index (uri),
  primary key (id)) ENGINE=InnoDB;

基本情報

  • 測定用PC
  • memcachedMySQL用のサーバ
  • ネットワーク環境
    100MbpsのSW-HUBに接続
  • 計測対象
    • ConcurrentHashMapは、測定用PCで計測
    • memcachedは、Fedora10で動作しており、ネットワーク経由でアクセス。
    • MySQLは、Fedora10で動作しており、ネットワーク経由でアクセス。

シンプルでカスタマイズ可能なWYSIWIGエディタの「wysihat」の使い方

ブラウザでWYSIWIGができるツールが無いか探していたところ、wysihatがシンプルでよさそうかなと思い、使ってみました。


インストールと環境構築

Fedora10でwysihat.jsの生成をおこないました。

必要なもの
  • rakeやrubyコマンド
  • gitコマンド
wysihat.jsを生成
$ git clone git://github.com/37signals/wysihat.git
$ cd wysihat
$ git submodule init    ← prototype, pdocなどのサブモジュールの初期化
$ git submodule update
$ rake                  ← wysihat.jsを生成
$ ls dist
prototype.js  wysihat.js
$ 

編集した内容をpostするには?

example/simple.htmlを改造してサーバにpostしようとしたのですが、うまく動きませんでした。

なぜ、編集した内容でpostされないのかわからなかったので、wysihat.jsのソースコードを調べました。

  1. WysiHatオブジェクトをformから参照できるようにする。下記の例だと、「var editor;]
  2. submitボタンの記述に「onclick="editor.save();"」を追加する。editor.save()を実行すると、iframe上で編集していた内容がtextareaにコピーされる。

下記がsimple.htmlを変更して、postできるようにしたソースです。

<html>
  <head>
  <script type="text/javascript" src="../dist/prototype.js"></script>
  <script type="text/javascript" src="../dist/wysihat.js"></script>

  <script type="text/javascript" charset="utf-8">
    var editor;     <!-- ☆☆要点 -->
    Event.observe(window, 'load', function() {
      editor = WysiHat.Editor.attach('content');
      var toolbar = new WysiHat.Toolbar(editor);
      toolbar.addButtonSet(WysiHat.Toolbar.ButtonSets.Basic);

      // Hide our error message if the editor loads fine
      $('error').hide();
    });
  </script>
  </head>
<body>
  <form action="http://<<your-cgi-path>>" method="post" style="width: 500px;">
    <textarea id="content" name="content"></textarea>
    <input type="submit" onclick="editor.save();" />  <!-- ☆☆要点 -->
  </form>
</body>
</html>

rails 2.3.2 でmemcachedにセッション情報を保存するには

rails2.3.2でmemcachedにセッション情報を保存する方法が少し変わったのでメモを残します。
セッション関連の設定は、config/initializers/session_store.rbに記述します。

ActionController::Base.session = {
   :memcache_server => 'memcachedのサーバ名:11211',
   :expire_after => 86400,
   :namespace => "あなたのrailsアプリ-#{ENV['RAILS_ENV']}",
} 
ActionController::Base.session_store = :mem_cache_store

以前の設定方法よりシンプルになりましたね。