jcunit's blog

JCUnitの開発日誌(ログ)です。"その時点での"JCUnit作者の理解や考え、開発状況を日本語で書きます。

Pluginインタフェースの整理

こちらのエントリでご紹介いただいたときに「デフォルトで設定されている各型ごとの水準一覧を取る手段がない」という指摘も頂いた。

uehaj.hatenablog.com

それ自体は、なんとかなりそうで、難しいものでもないのでこんなチケットを作った。

github.com

で、こんなふうに

  class DefaultValues {
    public static final DefaultValues INSTANCE = new DefaultValues();

    @FactorField
    public final Object defaultValues = null;

    private DefaultValues() {
    }

    public boolean[] booleanLevels() {
      return get().booleanLevels();
    }

    public byte[] byteLevels() {
      return get().byteLevels();
    }

    public char[] charLevels() {
      return get().charLevels();
    }
    // 大体こんな雰囲気になるとは思うが、最終的には変更になりそうなので、注意。
  }

jcunit/FactorField.java at 0.5.x-develop · dakusui/jcunit · GitHub

すればよかろうと思う。 が、LevelsProviderプラグインのインタフェースもついでに見直そうと思うとこれがはまるはまる。 しかし、0.6.0は安定版にしたいので、今、綺麗にするしかない。

0.5.5のRelease contentsはこのIssue-#22、各プラグインインタフェースの整理、あとはドキュメントの整理ってあたりになるかなあ。今週末に出したいところだ。

ConstraintManagerとIPO2TupleGenerator

IPOはテストケース(Tuple)内の因子(Factor)の水準(Level)を一個ずつ、すべての可能な組み合わせが網羅するように決めていくアルゴリズム。生成されるテストスイートは縦方向(テストケース数)と横方向(因子数)を必要に応じて交互に増加させながら成長していく。

大まかに言うと一番左のt個について可能な組み合わせをまず全部列挙する。この件数をHとしよう。
次にt+1個目をすでに決まったt個の右側にくっつける。このときその水準はできるだけt-wayカバレッジが上がるように選ぶ。もし、このときまでにすでにあるH個のテストケースだけでは可能なすべてのt-wayタプルを網羅できてなければ、網羅できていないt-wayタプルをもとにテストケースを増やしていく。
これを繰り返して全部の因子の処理が終わったらテストスイートの生成が完了するというわけだ。

タプル内のキーが全部出揃ってから制約検査を行うのは、生成されるテストスイートの品質や性能の観点で現実的ではない。
そこで、JCUnitでは中途半端な状態のタプルでもしようがないのでまずはConstraintManager#checkを使って検査を行うことにしている。

すると、ConstraintManager#checkが制約検査を行うにあたって必要な因子の水準が決まっていないということがありえる。
この場合、trueでもfalseでもないのでUndefinedSymbolを投げましょうというのがJCUnitの設計。
IPO2TupleGeneratorはUndefinedSymbolを検知した場合ひとまず別の因子の水準を決めて再びcheckメソッドにお伺いを立てることを繰り返す。

一応、そのへんのことをConstraintManager#checkメソッドJavadocにも書いてあったのだが、わかりにくいところと間違い(例外クラス名)があったので、以下のように明確化する。

  /**
   * Returns {@code true} if the given tuple doesn't violate any known constraints.
   * In case tuple doesn't have sufficient attribute values to be evaluated,
   * a {@code UndefinedSymbol} will be thrown.
   * Otherwise, {@code false} should be returned.
   *
   * @param tuple A tuple to be evaluated.
   * @return {@code true} - The tuple doesn't violate any constraints managed by this object / {@code false} - The tuple DOES violate a constraint.
   * @throws com.github.dakusui.jcunit.exceptions.UndefinedSymbol Failed to evaluate the tuple for insufficient attribute(s).
   */
  boolean check(Tuple tuple) throws UndefinedSymbol;

この修正は次回のリリース(0.5.5)には含まれるようにする。なおこの部分に関する変更はドキュメントに対してだけのものであり、制約検査やテストケース生成の挙動に変化は生じない。

実を言えば制約が定義されたもとで効率的に t-wiseのテストスイート生成を行うアルゴリズムはホットな研究領域であり、制約検査とテストスイート生成を独立に行えることを前提としたこのインタフェース設計は野心的というか楽観的なものなのだ。

参考:
IWCT 2015 : Fourth International Workshop on Combinatorial Testing

IPOは非常に面白いアルゴリズムなのだが、制約をどのように取り扱うべきかということになると、こうやってややこしい問題が生じてくる。
そもそも、ある論理式を満たすタプルを見つけましょうってSAT問題そのものですよね。と。そこにさらにどういう順番で因子を処理するべきかとかそういう話が加わってくるわけだから非常に悩ましい。
t-wayでテストスイートを生成するアルゴリズムにはAETGというものもあり、一件のテストケースの全因子の水準をいっぺんに決めるやり方だ。この点ではおそらく制約処理機能を実装する上ではいくらかはやりやすいアルゴリズムと言える。

こんなチケットを先日作ったのは、そんな背景がある。

AETG algorithm support · Issue #17 · dakusui/jcunit · GitHub

JCUnit 0.5.4リリース。

TupleGeneratorを直接取り扱いたい人々が一定数いるように見受けられるので、そのためのクラスやメソッドを整理してみた。

        TupleGenerator tg = new TupleGenerator.Builder().setFactors(
            new Factors.Builder()
                .add("OS", "Windows", "Linux")
                .add("Browser", "Chrome", "Firefox")
                .add("Bits", "32", "64").build()
        ).build();
        for (Tuple each : tg) {
          ps.println(each);
        }

TupleGeneratorを明示的に指定するときはこう。

    TupleGenerator tg = new TupleGenerator.Builder(
        IPO2TupleGenerator.class, 
        ConstraintManager.DEFAULT_CONSTRAINT_MANAGER
    ).setFactors(new Factors.Builder()
        ...

その他の変更点は以下。

Enhancements

  1. Local constraints:
  2. Support overloading methods with the same number of arguments
  3. Multi-threading support

Fixed bugs

  1. Story objects are not refreshed before each test case is executed. Due to this issue, nested FSMs might not be tested when there is more than one test method in a test class.

JCUnit 0.5.1 リリース(更新)

(早速機能拡張をしてるので、10/5現在の最新版は0.5.4です。
JCUnit 0.5.4リリース。 - jcunit's blog

今回の目玉は何といってもFSM(有限状態機械)のサポートだ。github.com


ユーザがSUT(テスト対象ソフトウェア)を有限状態機械としてモデルすると、pairwiseを使って、カバレッジもよくて件数もそんなに多くないテストスイートを自動でつくってそのまま実行してくれるという(私にとって)夢のような機能だ。
まあ、ほんとにカバレッジが十分にいいかとかは、これからちゃんと評価するのだけれど。
実際にはテストケースの生成はpairwiseでなくても別によいので、JCUnitによるモデルベーステスティング(Model-based testing, MBT)のサポートと言ってもよい。

三月くらいにJCUnitによるFSMサポートを作っていたときはこんなもんちょっと頑張ればあっという間だろうと思っていたのだが、仕事が忙しくなったり、入れ子になった有限状態機械を取り扱う仕組みがなかなかよくならなかったりでずいぶんとてこずってしまった。

こつこつと作りつづけ、一応は形になったので、0.5.1としてリリースする。
てこずっただけあり、実用上(少なくとも自分に)必要な機能はこれで網羅できたと思う。ドキュメントも頑張って書いたので、以前に書いたブログのエントリよりもわかりやすいと思う。英語だけど。
mavenのcoordinateは以下。

    <dependency>
      <groupId>com.github.dakusui</groupId>
      <artifactId>jcunit</artifactId>
      <version>0.5.4</version>
    </dependency>

(この数日で早速バグフィックスや拡張を入れて0.5.4をリリースしているので、更新しました)

テストは整理して充実させる必要があるし、若干迷っている箇所もある。(といっても外部仕様に影響しそうなのは@ParametersSpecの仕様だけだが)
Future worksのセクションに書いた点のうちいくつかは0.5.xの間にサポートされるかもしれない。

なんにしても、0.5.xを使って実例を積み重ね、そののちに0.6.xをリリースすることにしたい。

思うにAnnotationは

MVCで言うならVのようなソフトウェアのプレゼンテーションとかUIとかを担う層とも言える。
モデルとビューを分離するのはビューへの変更は頻繁に起こることから来ていて、そしてそれはユーザの選好に大きく依存するからなわけだ。

するとアノテーションの体系はユーザの選好に基づいて組み立てられてしかるべき(と言えそう)なのだが、APIライブラリのインタフェースとしてアノテーションを使う以上、そんなことはできないよね。

アノテーションもクラスなので、モデルに現れるクラス名との衝突を回避するために自然な名前を使いにくいというのも困る。(無論パッケージが違うので全くできないということではないのだが、一つのファイル内でimport文によってFQCNを省略できる同名のクラスはひとつだけだ。)

世間の人々はどうして(どう考えて)いるのだろう。

再始動

前回のポストからずいぶん時間が経ってしまった。
FSMサポートについてのこれまでの設計を見直したいと思ったのと、他のことに興味を奪われていたためだ。

間は開いてしまったもののもうすぐ前回の続きを書けると思う。