jcunit's blog

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

制約処理について(承前)

JCUnitでいかにして制約処理(禁則処理)を行っているか?結局のところ、以下の実装がその本質である。

jcunit/IpoGplus.java at 0.8.x-develop · dakusui/jcunit · GitHub

組み合わせテストにおいて、テストスイートの生成と制約処理は同時並行で行わなくてはならない。なぜか?

素朴には、まず制約を度外視してテストスイートを生成しておき、制約に違反するテストケースを取り除き、カバーされていないタプルのために新しいテストケースを生成・・・このプロセスを全ての可能なタプルが網羅されるまで反復する。というアルゴリズムが考えられる。

しかし、この過程は実用的な時間で終わるのだろうか?

実験や調査をしたことはないが、これははなはだ疑問なのである。 なぜかといえば、組み合わせテストにおいてテストケースは、テストスイートができるだけ小さくなるよう生成される。その一方、制約は複雑になりがちである。このため制約を考慮せずに生成したテストスイートはそこに含まれるテストケースの大部分、もしくは全部が制約に違反していることがあり得るからである。

上述の実装は、このことを踏まえて設計されている。基本的なテストスイート生成アルゴリズムIPO-Gと呼ばれるものである。テストスイート生成の過程で、制約に違反したタプルは網羅されるべき組み合わせから除外しておき、制約に違反しないテストケースを生成する工夫が組み込まれている。性能の最適化のために、制約に適合する組み合わせを選択するための処理はメモ化されていたりとかの工夫もしている。

私や私のチームで使用している限りでは、今のところ実用的な性能を発揮している。しかし残念ながら、NISTのACTSには速度上まったく及ばない。制約定義方法の柔軟さではかなりの優位性を主張できるが。

もしも、このアルゴリズムについてのさらなる解説に興味をお持ちの場合、このブログにその旨のコメントを残して欲しい。

「禁則処理」について

Constraint Handlingについてだが、率直にいう。0.7.xの制約処理(禁則処理)はたぶん、バグだらけだ。 JCUnitで制約を取り扱う必要があるなら、0.8.xを使って欲しい。

どちらもIPO-G系のアルゴリズムに制約処理を埋め込んだもので、制約に違反するタプルが母体アルゴリズムによって選ばれると、制約回避のためのロジックが実行される作りだ。0.8.xではJava8の恩恵を素直に活かして簡潔な実装になっているし、メモ化による高速化の恩恵も受けている。0.7.xのロジックは複雑で、自然言語では説明しがたい。テストはしているが、設計自体が言語化しにくいのでおそらく不十分だろう。

また、制約を取り扱うためのインタフェースも0.8.xはかなりわかりやすいはずだ。今から使いはじめてくれる人がJCUnit 0.7.xを使う理由はほぼないと思う。

ぜひ0.8.xを使って欲しい。最近、「いわゆる禁則処理。」という過去のエントリへのアクセスが多いようなので、告知がてらポストする次第である。

近況

ICST 2017で発表や大学院進学(社会人博士課程)等色々あったのだが、こちらのブログではなんにも報告していなかった。 その一方、アクセスログを見るとゴールデンウィーク後、それなりの数のアクセスを頂いているようだ。

このブログは開発記録として運営してきた面があり、記載された情報は必ずしも最新ではない。 特にICST 2017での発表後、JCUnitのAPIを一新している。このため以前のバージョンとの互換性は失われ、プラットフォームもJava 8以降限定となっている。これに伴って現行のテストランナーは現在JCUnit8と呼ばれている。

自分で言うことではないかも知れないが、現行の仕様は以前よりも遥かに洗練されており、柔軟になっている。これについては近々何らかの形で発表することにしたい。

アクセスのうちの何割かは、JCUnitを使ってみたいという人々によるものかもしれない。 現状、十分なドキュメントがあると言える状況ではないが、それでも少しでも実際に使う際に理解の助けになりそうな例や、リンクを以下にあげたい。

JCUnitはPICTよりも制約の取り扱いに長けている(ようだ)

githubにあるPICTレポジトリで報告されているが、PICTには以下のモデルからのテストスイート生成が終わらないという問題がある。

Eingangskanal: EVA_Anlageberatung, EVA_Order, EVA_Sonderweg, EVA_Sparplan, EVA_Neuemission, EVA_Direkteinstieg, HOST_T19000, HOST_T19001, HOST_T19901, HOST_T19750, HOST_T28900, Onlinebanking_PC, Onlinebanking_MSB, Onlinebanking_Neuemission_PC, Onlinebanking_Neuemission_MSB, Onlinebanking_Tablet_PC, Infobroker, commerzbank_de_pib
Finanzinstrument: Aktie, Unstrukturierte_Anleihe, strukturierte_Anleihe, Inv.Fonds, OIF, Zertifikat, Optionsschein, Xetra_Gold_ETC, EMISID
Produktzyklus: Neuemission_Information, Neuemission_offen, Neuemission_geschlossen, Neuemission_abgerechnet, Sekundaermarkt, n/a
Dienstleistungsart: Anlageberatung, beratungsfreies_Geschaeft, n/a
Orderart: Kauf(Beratungsdatum_gueltig), Kauf_(ohne_Beratungsdatum), Verkauf, aenderung, Streichung, Storno, Berichtigungsauftrag, n/a
Initiator: Kunde, Bank, n/a
Auftragserteilung: telefonisch, persoenlich, schriftlich, Haustuergeschaeft, n/a
Bereitstellungsdokumente: Beratungsprotokoll_Kunde, Beratungsprotokoll_Interessent, Beratungsprotokoll_Potenzial, Nachtraeglicher_KID-Versand, n/a
UDAL-Status: Normalbetrieb, Back_up-Betrieb, PRIIP,ohne_KID, WKN_nicht_vorhanden, WKN_inaktiv/geloescht, defektes_Dokument, Virus, non-PRIIP_auf_Blackliste, PRIIP_auf_Blackliste, UDAL_nicht_verfuegbar, n/a
Abruf_Infoblaetter: WA_von_FWW, PIB_von_DOTi, PIB_von_C&M-FIC, PIB_von_PC_PM, KID_von_PC_PM, KID_von_DOTi, KID_von_externem_Hersteller

IF [Eingangskanal] = "HOST_T19901"
THEN [Orderart] <> "Kauf_(Beratungsdatum_gueltig)"
AND [Produktzyklus] <> "Neuemission_offen"
AND [Produktzyklus] <> "n/a"
AND [Dienstleistungsart] <> "n/a"
AND [Orderart] <> "n/a"
AND [Initiator] = "n/a"
AND [Auftragserteilung] = "n/a"
AND [Bereitstellungsdokumente]= "n/a"
AND [UDAL-Status] = "n/a";

IF [Eingangskanal] in {"HOST_T19000", "HOST_T19001", "HOST_T19750", "HOST_T28900"}
THEN [Produktzyklus] <> "Neuemission_Information"
AND [Produktzyklus] <> "Neuemission_offen"
AND [Produktzyklus] <> "n/a"
AND [Dienstleistungsart] <> "n/a"
AND [Orderart] <> "n/a"
AND [Initiator] = "n/a"
AND [Auftragserteilung] = "n/a"
AND [Bereitstellungsdokumente]= "n/a"
AND [UDAL-Status] = "n/a";

IF [Eingangskanal] like "EVA_*"
THEN [Initiator] <> "n/a"
AND [Produktzyklus] <> "n/a"
AND [Dienstleistungsart] <> "n/a"
AND [Orderart] <> "n/a"
AND [Auftragserteilung] <> "n/a"
AND [Bereitstellungsdokumente] <> "n/a"
AND [UDAL-Status] <> "n/a";

IF [Eingangskanal] IN {"Onlinebanking_PC", "Onlinebanking_MSB", "Onlinebanking_Tablet_PC"}
THEN [Produktzyklus] <> "Neuemission_Information"
AND [Produktzyklus] <> "Neuemission_offen"
AND [Produktzyklus] <> "n/a"
AND [Dienstleistungsart] <> "n/a"
AND [Orderart] <> "n/a";

IF [Eingangskanal] like "Onlinebanking*"
THEN [Dienstleistungsart]="beratungsfreies_Geschaeft"
AND [Produktzyklus] <> "n/a"
AND [Dienstleistungsart] <> "n/a"
AND [Orderart] <> "Storno"
AND [Orderart] <> "Berichtigungsauftrag"
AND [Orderart] <> "Kauf_(Beratungsdatum_gueltig)"
AND [Orderart] <> "n/a"
AND [Initiator] = "n/a"
AND [Auftragserteilung] = "n/a"
AND [Bereitstellungsdokumente] = "n/a"
AND [UDAL-Status] <>"Back_up-Betrieb";

IF [Eingangskanal] IN {"EVA_Sonderweg", "EVA_Sparplan", "EVA_Direkteinstieg", "EVA Order"}
THEN [Produktzyklus] <>"Neuemission_Information"
AND [Produktzyklus] <>"Neuemission_offen"
AND [Produktzyklus] <>"n/a"
AND [Dienstleistungsart] <>"n/a"
AND [Orderart] <>"n/a";

IF [Eingangskanal] IN {"EVA_Sonderweg", "EVA_Sparplan", "EVA_Direkteinstieg"}
THEN [Orderart] <>"Storno"
AND [Orderart] <>"Berichtigungsauftrag"
AND [Orderart] <>"Kauf_(Beratungsdatum_gueltig)"
AND [Produktzyklus] <>"n/a"
AND [Dienstleistungsart] <>"n/a"
AND [Orderart] <>"n/a";

IF [Eingangskanal] in {"EVA_Anlageberatung", "EVA_Neuemission", "EVA_Order"}
THEN [Orderart] <>"Storno"
AND [Orderart] <>"Berichtigungsauftrag"
AND [Produktzyklus] <>"n/a"
AND [Dienstleistungsart] <>"n/a"
AND [Orderart] <>"n/a";

IF [Eingangskanal] IN {"Infobroker", "commerzbank_de_pib"}
THEN [Finanzinstrument] <> "EMISID"
AND [Produktzyklus] = "n/a"
AND [Dienstleistungsart] = "n/a"
AND [Orderart] = "n/a"
AND [Initiator] = "n/a"
AND [Auftragserteilung] = "n/a"
AND [Bereitstellungsdokumente]= "n/a"
AND [UDAL-Status] <> "Back_up-Betrieb";

試してみると、実際、2時間半以上かかっても終わらず現在も計算中だ。PICTの作者であるJacekによると、計算時間がexponentialになっちゃうのでしょうがないんだよということだ。それも無理からぬことで、これは要するにSAT問題(NP完全)を解きながらできるだけ小さなテストスイートの生成を行わなければいけないのだから、どうしたもんかなー、と考えこんでしまうところだ。

なのだけれど、現実世界の制約条件ってこのくらいにはなるよね、と思うし、因子数は10、各水準数もそれほど多くなく、総当りでテストケースを作るとしても134,719,200通りしかありえない。さらによく見てみると、因子Abruf_Infoblaetterは制約に全然関与しない。

このくらいだったらJCUnitではなんとかなるのではないか、と思い試してみた。JCUnitで同じモデルを実装すると以下のようになる。

@RunWith(JCUnit8.class)
public class Issue13 {
  /**
   * Eingangskanal:
   * <p>
   * EVA_Anlageberatung, EVA_Order, EVA_Sonderweg, EVA_Sparplan, EVA_Neuemission,
   * EVA_Direkteinstieg, HOST_T19000, HOST_T19001, HOST_T19901, HOST_T19750,
   * HOST_T28900, Onlinebanking_PC, Onlinebanking_MSB, Onlinebanking_Neuemission_PC,
   * Onlinebanking_Neuemission_MSB, Onlinebanking_Tablet_PC, Infobroker, commerzbank_de_pib
   */
  @ParameterSource
  public Parameter.Factory<String> eingangskanal() {
    return Parameters.simple(
        "EVA_Anlageberatung", "EVA_Order", "EVA_Sonderweg", "EVA_Sparplan", "EVA_Neuemission",
        "EVA_Direkteinstieg", "HOST_T19000", "HOST_T19001", "HOST_T19901", "HOST_T19750",
        "HOST_T28900", "Onlinebanking_PC", "Onlinebanking_MSB", "Onlinebanking_Neuemission_PC",
        "Onlinebanking_Neuemission_MSB", "Onlinebanking_Tablet_PC", "Infobroker", "commerzbank_de_pib"
    );
  }

  /**
   * Finanzinstrument:
   * <p>
   * Aktie, Unstrukturierte_Anleihe, strukturierte_Anleihe, Inv.Fonds, OIF,
   * Zertifikat, Optionsschein, Xetra_Gold_ETC, EMISID
   */
  @ParameterSource
  public Parameter.Factory<String> finanzinstrument() {
    return Parameters.simple(
        "Aktie", "Unstrukturierte_Anleihe", "strukturierte_Anleihe", "Inv.Fonds", "OIF",
        "Zertifikat", "Optionsschein", "Xetra_Gold_ETC", "EMISID"
    );
  }

  /**
   * Produktzyklus:
   * <p>
   * Neuemission_Information, Neuemission_offen, Neuemission_geschlossen, Neuemission_abgerechnet,
   * Sekundaermarkt, n/a
   */
  @ParameterSource
  public Parameter.Factory<String> produktzyklus() {
    return Parameters.simple(
        "Neuemission_Information", "Neuemission_offen", "Neuemission_geschlossen", "Neuemission_abgerechnet",
        "Sekundaermarkt", "n/a"
    );
  }

  /**
   * Dienstleistungsart: Anlageberatung, beratungsfreies_Geschaeft, n/a
   */
  @ParameterSource
  public Parameter.Factory<String> dienstleistungsart() {
    return Parameters.simple(
        "Anlageberatung", "beratungsfreies_Geschaeft", "n/a"
    );
  }

  /**
   * Orderart:
   * Kauf(Beratungsdatum_gueltig), Kauf_(ohne_Beratungsdatum), Verkauf,
   * aenderung, Streichung, Storno, Berichtigungsauftrag, n/a
   */
  @ParameterSource
  public Parameter.Factory<String> orderart() {
    return Parameters.simple(
        "Kauf(Beratungsdatum_gueltig)", "Kauf_(ohne_Beratungsdatum)", "Verkauf",
        "aenderung", "Streichung", "Storno", "Berichtigungsauftrag", "n/a"
    );
  }

  /**
   * Initiator: Kunde, Bank, n/a
   */
  @ParameterSource
  public Parameter.Factory<String> initiator() {
    return Parameters.simple(
        "Kunde", "Bank", "n/a"
    );
  }

  /**
   * Auftragserteilung: telefonisch, persoenlich, schriftlich, Haustuergeschaeft, n/a
   */
  @ParameterSource
  public Parameter.Factory<String> auftragserteilung() {
    return Parameters.simple(
        "telefonisch", "persoenlich", "schriftlich", "Haustuergeschaeft", "n/a"
    );
  }

  /**
   * Bereitstellungsdokumente:
   * <p>
   * Beratungsprotokoll_Kunde, Beratungsprotokoll_Interessent, Beratungsprotokoll_Potenzial,
   * Nachtraeglicher_KID-Versand, n/a
   */
  @ParameterSource
  public Parameter.Factory<String> bereitstellungsdokumente() {
    return Parameters.simple(
        "Beratungsprotokoll_Kunde", "Beratungsprotokoll_Interessent", "Beratungsprotokoll_Potenzial",
        "Nachtraeglicher_KID-Versand", "n/a"
    );
  }

  /**
   * UDAL-Status:
   * <p>
   * Normalbetrieb, Back_up-Betrieb, PRIIP,ohne_KID, WKN_nicht_vorhanden, WKN_inaktiv/geloescht,
   * defektes_Dokument, Virus, non-PRIIP_auf_Blackliste, PRIIP_auf_Blackliste, UDAL_nicht_verfuegbar,
   * n/a
   */
  @ParameterSource
  public Parameter.Factory<String> udalStatus() {
    return Parameters.simple(
        "Normalbetrieb", "Back_up-Betrieb", "PRIIP", "ohne_KID", "WKN_nicht_vorhanden",
        "WKN_inaktiv/geloescht", "defektes_Dokument", "Virus", "non-PRIIP_auf_Blackliste",
        "PRIIP_auf_Blackliste", "UDAL_nicht_verfuegbar", "n/a"
    );
  }

  /**
   * Abruf_Infoblaetter:
   * <p>
   * WA_von_FWW, PIB_von_DOTi, PIB_von_C&M-FIC, PIB_von_PC_PM,
   * KID_von_PC_PM, KID_von_DOTi, KID_von_externem_Hersteller
   */
  @ParameterSource
  public Parameter.Factory<String> abruf_Infoblaetter() {
    return Parameters.simple(
        "WA_von_FWW", "PIB_von_DOTi", "PIB_von_C&M-FIC", "PIB_von_PC_PM",
        "KID_von_PC_PM", "KID_von_DOTi", "KID_von_externem_Hersteller"
    );
  }

  ////
  //1
  //
  //IF [Eingangskanal] = "HOST_T19901"
  //THEN [Orderart] <> "Kauf_(Beratungsdatum_gueltig)"
  //AND [Produktzyklus] <> "Neuemission_offen"
  //AND [Produktzyklus] <> "n/a"
  //AND [Dienstleistungsart] <> "n/a"
  //AND [Orderart] <> "n/a"
  //AND [Initiator] = "n/a"
  //AND [Auftragserteilung] = "n/a"
  //AND [Bereitstellungsdokumente]= "n/a"
  //AND [UDAL-Status] = "n/a";
  @Condition(constraint = true)
  public boolean constraint1(
      @From("eingangskanal") String eingangskanal,
      @From("orderart") String orderart,
      @From("produktzyklus") String produktzyklus,
      @From("dienstleistungsart") String dienstleistungsart,
      @From("initiator") String initiator,
      @From("auftragserteilung") String auftragserteilung,
      @From("bereitstellungsdokumente") String bereitstellungsdokumente,
      @From("udalStatus") String udalStatus
  ) {
    //noinspection SimplifiableIfStatement
    if (Objects.equals(eingangskanal, "HOST_T19901"))
      return (
          !Objects.equals(orderart, "Kauf_(Beratungsdatum_gueltig)") &&
              !Objects.equals(produktzyklus, "Neuemission_offen") &&
              !Objects.equals(produktzyklus, "n/a") &&
              !Objects.equals(dienstleistungsart, "n/a") &&
              !Objects.equals(orderart, "n/a") &&
              Objects.equals(initiator, "n/a") &&
              Objects.equals(auftragserteilung, "n/a") &&
              Objects.equals(bereitstellungsdokumente, "n/a") &&
              Objects.equals(udalStatus, "n/a")
      );
    return true;
  }

  ////2
  //
  //IF [Eingangskanal] in {"HOST_T19000", "HOST_T19001", "HOST_T19750", "HOST_T28900"}
  //THEN [Produktzyklus] <> "Neuemission_Information"
  //AND [Produktzyklus] <> "Neuemission_offen"
  //AND [Produktzyklus] <> "n/a"
  //AND [Dienstleistungsart] <> "n/a"
  //AND [Orderart] <> "n/a"
  //AND [Initiator] = "n/a"
  //AND [Auftragserteilung] = "n/a"
  //AND [Bereitstellungsdokumente]= "n/a"
  //AND [UDAL-Status] = "n/a";
  @Condition(constraint = true)
  public boolean constraint2(
      @From("eingangskanal") String eingangskanal,
      @From("orderart") String orderart,
      @From("produktzyklus") String produktzyklus,
      @From("dienstleistungsart") String dienstleistungsart,
      @From("initiator") String initiator,
      @From("auftragserteilung") String auftragserteilung,
      @From("bereitstellungsdokumente") String bereitstellungsdokumente,
      @From("udalStatus") String udalStatus
  ) {
    if (asList("HOST_T19000", "HOST_T19001", "HOST_T19750", "HOST_T28900").contains(eingangskanal)) {
      return !produktzyklus.equals("Neuemission_Information") &&
          !produktzyklus.equals("Neuemission_offen") &&
          !produktzyklus.equals("n/a") &&
          !dienstleistungsart.equals("n/a") &&
          !orderart.equals("n/a") &&
          initiator.equals("n/a") &&
          auftragserteilung.equals("n/a") &&
          bereitstellungsdokumente.equals("n/a") &&
          udalStatus.equals("n/a");
    }
    return true;
  }

  ////3
  //
  //IF [Eingangskanal] like "EVA_*"
  //THEN [Initiator] <> "n/a"
  //AND [Produktzyklus] <> "n/a"
  //AND [Dienstleistungsart] <> "n/a"
  //AND [Orderart] <> "n/a"
  //AND [Auftragserteilung] <> "n/a"
  //AND [Bereitstellungsdokumente] <> "n/a"
  //AND [UDAL-Status] <> "n/a";
  @Condition(constraint = true)
  public boolean constraint3(
      @From("eingangskanal") String eingangskanal,
      @From("orderart") String orderart,
      @From("produktzyklus") String produktzyklus,
      @From("initiator") String initiator,
      @From("auftragserteilung") String auftragserteilung,
      @From("bereitstellungsdokumente") String bereitstellungsdokumente,
      @From("udalStatus") String udalStatus
  ) {
    if (eingangskanal.startsWith("EVA_"))
      return !initiator.equals("n/a") &&
          !produktzyklus.equals("n/a") &&
          !orderart.equals("n/a") &&
          !auftragserteilung.equals("n/a") &&
          !bereitstellungsdokumente.equals("n/a") &&
          !udalStatus.equals("n/a");
    return true;
  }

  ////4
  //
  //IF [Eingangskanal] IN {"Onlinebanking_PC", "Onlinebanking_MSB", "Onlinebanking_Tablet_PC"}
  //THEN [Produktzyklus] <> "Neuemission_Information"
  //AND [Produktzyklus] <> "Neuemission_offen"
  //AND [Produktzyklus] <> "n/a"
  //AND [Dienstleistungsart] <> "n/a"
  //AND [Orderart] <> "n/a";
  @Condition(constraint = true)
  public boolean constraint4(
      @From("eingangskanal") String eingangskanal,
      @From("orderart") String orderart,
      @From("produktzyklus") String produktzyklus,
      @From("dienstleistungsart") String dienstleistungsart
  ) {
    if (asList("Onlinebanking_PC", "Onlinebanking_MSB", "Onlinebanking_Tablet_PC").contains(eingangskanal))
      return !produktzyklus.equals("Neuemission_Information") &&
          !produktzyklus.equals("Neuemission_offen") &&
          !produktzyklus.equals("n/a") &&
          !dienstleistungsart.equals("n/a") &&
          !orderart.equals("n/a");
    return true;
  }

  ////5
  //
  //IF [Eingangskanal] like "Onlinebanking*"
  //THEN [Dienstleistungsart]="beratungsfreies_Geschaeft"
  //AND [Produktzyklus] <> "n/a"
  //AND [Dienstleistungsart] <> "n/a"
  //AND [Orderart] <> "Storno"
  //AND [Orderart] <> "Berichtigungsauftrag"
  //AND [Orderart] <> "Kauf_(Beratungsdatum_gueltig)"
  //AND [Orderart] <> "n/a"
  //AND [Initiator] = "n/a"
  //AND [Auftragserteilung] = "n/a"
  //AND [Bereitstellungsdokumente] = "n/a"
  //AND [UDAL-Status] <>"Back_up-Betrieb";
  @Condition(constraint = true)
  public boolean constraint5(
      @From("eingangskanal") String eingangskanal,
      @From("orderart") String orderart,
      @From("produktzyklus") String produktzyklus,
      @From("dienstleistungsart") String dienstleistungsart,
      @From("initiator") String initiator,
      @From("auftragserteilung") String auftragserteilung,
      @From("bereitstellungsdokumente") String bereitstellungsdokumente,
      @From("udalStatus") String udalStatus
  ) {
    if (eingangskanal.startsWith("Onlinebanking"))
      return dienstleistungsart.equals("beratungsfreies_Geschaeft") &&
          !produktzyklus.equals("n/a") &&
          !dienstleistungsart.equals("n/a") &&
          !orderart.equals("Storno") &&
          !orderart.equals("Berichtigungsauftrag") &&
          !orderart.equals("Kauf_(Beratungsdatum_gueltig)") &&
          !orderart.equals("n/a") &&
          initiator.equals("n/a") &&
          auftragserteilung.equals("n/a") &&
          bereitstellungsdokumente.equals("n/a") &&
          !udalStatus.equals("Back_up-Betrieb");
    return true;
  }

  ////6
  //IF [Eingangskanal] IN {"EVA_Sonderweg", "EVA_Sparplan", "EVA_Direkteinstieg", "EVA Order"}
  //THEN [Produktzyklus] <>"Neuemission_Information"
  //AND [Produktzyklus] <>"Neuemission_offen"
  //AND [Produktzyklus] <>"n/a"
  //AND [Dienstleistungsart] <>"n/a"
  //AND [Orderart] <>"n/a";
  @Condition(constraint = true)
  public boolean constraint6(
      @From("eingangskanal") String eingangskanal,
      @From("orderart") String orderart,
      @From("produktzyklus") String produktzyklus,
      @From("dienstleistungsart") String dienstleistungsart
  ) {
    if (asList("EVA_Sonderweg", "EVA_Sparplan", "EVA_Direkteinstieg", "EVA Order").contains(eingangskanal))
      return !produktzyklus.equals("Neuemission_Information") &&
          !produktzyklus.equals("Neuemission_offen") &&
          !produktzyklus.equals("n/a") &&
          !dienstleistungsart.equals("n/a") &&
          !orderart.equals("n/a");
    return true;
  }

  //7
  //IF [Eingangskanal] IN {"EVA_Sonderweg", "EVA_Sparplan", "EVA_Direkteinstieg"}
  //THEN [Orderart] <>"Storno"
  //AND [Orderart] <>"Berichtigungsauftrag"
  //AND [Orderart] <>"Kauf_(Beratungsdatum_gueltig)"
  //AND [Produktzyklus] <>"n/a"
  //AND [Dienstleistungsart] <>"n/a"
  //AND [Orderart] <>"n/a";
  @Condition(constraint = true)
  public boolean constraint7(
      @From("eingangskanal") String eingangskanal,
      @From("orderart") String orderart,
      @From("produktzyklus") String produktzyklus,
      @From("dienstleistungsart") String dienstleistungsart
  ) {
    if (asList("EVA_Sonderweg", "EVA_Sparplan", "EVA_Direkteinstieg").contains(eingangskanal))
      return !orderart.equals("Storno") &&
          !orderart.equals("Berichtigungsauftrag") &&
          !orderart.equals("Kauf_(Beratungsdatum_gueltig)") &&
          !produktzyklus.equals("n/a") &&
          !dienstleistungsart.equals("n/a") &&
          !orderart.equals("n/a");
    return true;
  }

  //8
  //IF [Eingangskanal] in {"EVA_Anlageberatung", "EVA_Neuemission", "EVA_Order"}
  //THEN [Orderart] <>"Storno"
  //AND [Orderart] <>"Berichtigungsauftrag"
  //AND [Produktzyklus] <>"n/a"
  //AND [Dienstleistungsart] <>"n/a"
  //AND [Orderart] <>"n/a";
  @Condition(constraint = true)
  public boolean constraint8(
      @From("eingangskanal") String eingangskanal,
      @From("orderart") String orderart,
      @From("produktzyklus") String produktzyklus,
      @From("dienstleistungsart") String dienstleistungsart
  ) {
    if (asList("EVA_Anlageberatung", "EVA_Neuemission", "EVA_Order").contains(eingangskanal))
      return !orderart.equals("Storno") &&
          !orderart.equals("Berichtigungsauftrag") &&
          !produktzyklus.equals("n/a") &&
          !dienstleistungsart.equals("n/a") &&
          !orderart.equals("n/a");
    return true;
  }

  //9
  //
  //IF [Eingangskanal] IN {"Infobroker", "commerzbank_de_pib"}
  //THEN [Finanzinstrument] <> "EMISID"
  //AND [Produktzyklus] = "n/a"
  //AND [Dienstleistungsart] = "n/a"
  //AND [Orderart] = "n/a"
  //AND [Initiator] = "n/a"
  //AND [Auftragserteilung] = "n/a"
  //AND [Bereitstellungsdokumente]= "n/a"
  //AND [UDAL-Status] <> "Back_up-Betrieb";
  @Condition(constraint = true)
  public boolean constraint9(
      @From("eingangskanal") String eingangskanal,
      @From("finanzinstrument") String finanzinstrument,
      @From("orderart") String orderart,
      @From("produktzyklus") String produktzyklus,
      @From("dienstleistungsart") String dienstleistungsart,
      @From("initiator") String initiator,
      @From("auftragserteilung") String auftragserteilung,
      @From("bereitstellungsdokumente") String bereitstellungsdokumente,
      @From("udalStatus") String udalStatus
  ) {
    if (asList("Infobroker", "commerzbank_de_pib").contains(eingangskanal))
      return !finanzinstrument.equals("EMISID") &&
          produktzyklus.equals("n/a") &&
          dienstleistungsart.equals("n/a") &&
          orderart.equals("n/a") &&
          initiator.equals("n/a") &&
          auftragserteilung.equals("n/a") &&
          bereitstellungsdokumente.equals("n/a") &&
          !udalStatus.equals("Back_up-Betrieb");
    return true;
  }

  @Test
  public void test(
      @From("eingangskanal") String eingangskanal,
      @From("finanzinstrument") String finanzinstrument,
      @From("orderart") String orderart,
      @From("produktzyklus") String produktzyklus,
      @From("dienstleistungsart") String dienstleistungsart,
      @From("initiator") String initiator,
      @From("auftragserteilung") String auftragserteilung,
      @From("bereitstellungsdokumente") String bereitstellungsdokumente,
      @From("udalStatus") String udalStatus,
      @From("abruf_Infoblaetter") String abruf_Infoblaetter
  ) {
    System.out.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s%n",
        eingangskanal,
        finanzinstrument,
        orderart,
        produktzyklus,
        dienstleistungsart,
        initiator,
        auftragserteilung,
        bereitstellungsdokumente,
        udalStatus,
        abruf_Infoblaetter
    );
  }
}

結局生成には2時間弱かかったが、テストスイートは以下のとおりになる。

// eingangskanal, finanzinstrument, orderart, produktzyklus, dienstleistungsart, initiator, auftragserteilung, bereitstellungsdokumente, udalStatus, abruf_Infoblaetter
EVA_Anlageberatung,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,Normalbetrieb,WA_von_FWW
EVA_Anlageberatung,Unstrukturierte_Anleihe,Kauf_(ohne_Beratungsdatum),Neuemission_offen,beratungsfreies_Geschaeft,Bank,persoenlich,Beratungsprotokoll_Interessent,Back_up-Betrieb,PIB_von_DOTi
EVA_Anlageberatung,strukturierte_Anleihe,Verkauf,Neuemission_geschlossen,Anlageberatung,Bank,schriftlich,Beratungsprotokoll_Potenzial,PRIIP,PIB_von_C&M-FIC
EVA_Anlageberatung,Inv.Fonds,aenderung,Neuemission_abgerechnet,Anlageberatung,Kunde,Haustuergeschaeft,Nachtraeglicher_KID-Versand,ohne_KID,PIB_von_PC_PM
EVA_Anlageberatung,OIF,Streichung,Sekundaermarkt,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Interessent,WKN_nicht_vorhanden,KID_von_PC_PM
EVA_Anlageberatung,Zertifikat,Kauf(Beratungsdatum_gueltig),Neuemission_offen,beratungsfreies_Geschaeft,Kunde,schriftlich,Nachtraeglicher_KID-Versand,WKN_inaktiv/geloescht,KID_von_DOTi
EVA_Anlageberatung,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,Kunde,persoenlich,Beratungsprotokoll_Kunde,defektes_Dokument,KID_von_externem_Hersteller
EVA_Anlageberatung,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_abgerechnet,beratungsfreies_Geschaeft,Bank,telefonisch,Beratungsprotokoll_Potenzial,Virus,PIB_von_PC_PM
EVA_Anlageberatung,EMISID,Kauf(Beratungsdatum_gueltig),Sekundaermarkt,beratungsfreies_Geschaeft,Bank,Haustuergeschaeft,Beratungsprotokoll_Kunde,non-PRIIP_auf_Blackliste,PIB_von_C&M-FIC
EVA_Anlageberatung,Aktie,Kauf_(ohne_Beratungsdatum),Neuemission_geschlossen,Anlageberatung,Kunde,Haustuergeschaeft,Beratungsprotokoll_Interessent,PRIIP_auf_Blackliste,KID_von_DOTi
EVA_Anlageberatung,Aktie,Verkauf,Neuemission_offen,beratungsfreies_Geschaeft,Bank,telefonisch,Nachtraeglicher_KID-Versand,UDAL_nicht_verfuegbar,KID_von_PC_PM
EVA_Order,Unstrukturierte_Anleihe,Verkauf,Neuemission_abgerechnet,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Kunde,Normalbetrieb,PIB_von_DOTi
EVA_Order,Aktie,aenderung,Sekundaermarkt,beratungsfreies_Geschaeft,Kunde,schriftlich,Beratungsprotokoll_Potenzial,Back_up-Betrieb,WA_von_FWW
EVA_Order,Inv.Fonds,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,Bank,telefonisch,Beratungsprotokoll_Interessent,PRIIP,KID_von_externem_Hersteller
EVA_Order,strukturierte_Anleihe,Kauf_(ohne_Beratungsdatum),Neuemission_Information,beratungsfreies_Geschaeft,Kunde,telefonisch,Nachtraeglicher_KID-Versand,ohne_KID,PIB_von_C&M-FIC
EVA_Order,Zertifikat,Kauf_(ohne_Beratungsdatum),Neuemission_geschlossen,Anlageberatung,Bank,Haustuergeschaeft,Beratungsprotokoll_Potenzial,WKN_nicht_vorhanden,WA_von_FWW
EVA_Order,OIF,Kauf_(ohne_Beratungsdatum),Neuemission_abgerechnet,Anlageberatung,Bank,schriftlich,Beratungsprotokoll_Kunde,WKN_inaktiv/geloescht,PIB_von_PC_PM
EVA_Order,Xetra_Gold_ETC,Streichung,Neuemission_offen,Anlageberatung,Bank,Haustuergeschaeft,Beratungsprotokoll_Kunde,defektes_Dokument,KID_von_DOTi
EVA_Order,Optionsschein,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,Kunde,persoenlich,Nachtraeglicher_KID-Versand,Virus,KID_von_PC_PM
EVA_Order,Aktie,Streichung,Neuemission_abgerechnet,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Potenzial,non-PRIIP_auf_Blackliste,KID_von_externem_Hersteller
EVA_Order,EMISID,Verkauf,Neuemission_Information,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Potenzial,PRIIP_auf_Blackliste,PIB_von_PC_PM
EVA_Order,Unstrukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,Kunde,schriftlich,Beratungsprotokoll_Interessent,UDAL_nicht_verfuegbar,PIB_von_DOTi
EVA_Sonderweg,strukturierte_Anleihe,aenderung,Neuemission_geschlossen,beratungsfreies_Geschaeft,Bank,telefonisch,Beratungsprotokoll_Interessent,Normalbetrieb,PIB_von_DOTi
EVA_Sonderweg,Inv.Fonds,Verkauf,Sekundaermarkt,Anlageberatung,Kunde,Haustuergeschaeft,Beratungsprotokoll_Kunde,Back_up-Betrieb,KID_von_PC_PM
EVA_Sonderweg,Aktie,Kauf_(ohne_Beratungsdatum),Neuemission_abgerechnet,Anlageberatung,Kunde,persoenlich,Nachtraeglicher_KID-Versand,PRIIP,WA_von_FWW
EVA_Sonderweg,Unstrukturierte_Anleihe,Streichung,Neuemission_geschlossen,beratungsfreies_Geschaeft,Bank,schriftlich,Beratungsprotokoll_Potenzial,ohne_KID,KID_von_PC_PM
EVA_Sonderweg,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_abgerechnet,beratungsfreies_Geschaeft,Bank,schriftlich,Beratungsprotokoll_Interessent,WKN_nicht_vorhanden,PIB_von_C&M-FIC
EVA_Sonderweg,Xetra_Gold_ETC,Verkauf,Neuemission_geschlossen,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Interessent,WKN_inaktiv/geloescht,WA_von_FWW
EVA_Sonderweg,OIF,Verkauf,Neuemission_geschlossen,beratungsfreies_Geschaeft,Kunde,telefonisch,Nachtraeglicher_KID-Versand,defektes_Dokument,KID_von_DOTi
EVA_Sonderweg,Zertifikat,Verkauf,Neuemission_geschlossen,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,Virus,KID_von_externem_Hersteller
EVA_Sonderweg,Unstrukturierte_Anleihe,aenderung,Neuemission_geschlossen,Anlageberatung,Kunde,telefonisch,Nachtraeglicher_KID-Versand,non-PRIIP_auf_Blackliste,PIB_von_PC_PM
EVA_Sonderweg,Unstrukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Sekundaermarkt,beratungsfreies_Geschaeft,Bank,telefonisch,Beratungsprotokoll_Kunde,PRIIP_auf_Blackliste,KID_von_externem_Hersteller
EVA_Sonderweg,EMISID,Kauf_(ohne_Beratungsdatum),Neuemission_geschlossen,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,UDAL_nicht_verfuegbar,KID_von_externem_Hersteller
EVA_Sparplan,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Neuemission_geschlossen,Anlageberatung,Kunde,schriftlich,Beratungsprotokoll_Potenzial,Normalbetrieb,KID_von_DOTi
EVA_Sparplan,strukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_abgerechnet,beratungsfreies_Geschaeft,Bank,telefonisch,Beratungsprotokoll_Kunde,Back_up-Betrieb,KID_von_PC_PM
EVA_Sparplan,Unstrukturierte_Anleihe,aenderung,Sekundaermarkt,Anlageberatung,Kunde,Haustuergeschaeft,Beratungsprotokoll_Kunde,PRIIP,PIB_von_DOTi
EVA_Sparplan,Aktie,Verkauf,Sekundaermarkt,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Interessent,ohne_KID,PIB_von_PC_PM
EVA_Sparplan,Xetra_Gold_ETC,aenderung,Sekundaermarkt,Anlageberatung,Kunde,persoenlich,Nachtraeglicher_KID-Versand,WKN_nicht_vorhanden,KID_von_externem_Hersteller
EVA_Sparplan,Optionsschein,Streichung,Sekundaermarkt,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Potenzial,WKN_inaktiv/geloescht,PIB_von_DOTi
EVA_Sparplan,Zertifikat,aenderung,Neuemission_abgerechnet,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Interessent,defektes_Dokument,PIB_von_C&M-FIC
EVA_Sparplan,OIF,aenderung,Neuemission_geschlossen,Anlageberatung,Kunde,Haustuergeschaeft,Beratungsprotokoll_Interessent,Virus,WA_von_FWW
EVA_Sparplan,strukturierte_Anleihe,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Interessent,non-PRIIP_auf_Blackliste,KID_von_DOTi
EVA_Sparplan,strukturierte_Anleihe,Streichung,Neuemission_abgerechnet,Anlageberatung,Kunde,schriftlich,Nachtraeglicher_KID-Versand,PRIIP_auf_Blackliste,WA_von_FWW
EVA_Sparplan,strukturierte_Anleihe,aenderung,Neuemission_abgerechnet,Anlageberatung,Kunde,Haustuergeschaeft,Beratungsprotokoll_Potenzial,UDAL_nicht_verfuegbar,KID_von_DOTi
EVA_Neuemission,OIF,Kauf(Beratungsdatum_gueltig),Neuemission_offen,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Potenzial,Normalbetrieb,PIB_von_C&M-FIC
EVA_Neuemission,Zertifikat,Streichung,Neuemission_Information,beratungsfreies_Geschaeft,Bank,Haustuergeschaeft,Nachtraeglicher_KID-Versand,Back_up-Betrieb,PIB_von_PC_PM
EVA_Neuemission,Optionsschein,Verkauf,Neuemission_offen,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,PRIIP,WA_von_FWW
EVA_Neuemission,Xetra_Gold_ETC,Kauf_(ohne_Beratungsdatum),Neuemission_Information,Anlageberatung,Kunde,schriftlich,Beratungsprotokoll_Kunde,ohne_KID,PIB_von_DOTi
EVA_Neuemission,Aktie,Verkauf,Neuemission_Information,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,WKN_nicht_vorhanden,KID_von_DOTi
EVA_Neuemission,Unstrukturierte_Anleihe,aenderung,Neuemission_Information,Anlageberatung,Kunde,Haustuergeschaeft,Beratungsprotokoll_Interessent,WKN_inaktiv/geloescht,KID_von_PC_PM
EVA_Neuemission,strukturierte_Anleihe,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,Kunde,schriftlich,Beratungsprotokoll_Potenzial,defektes_Dokument,KID_von_externem_Hersteller
EVA_Neuemission,Inv.Fonds,Streichung,Neuemission_offen,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Kunde,Virus,PIB_von_C&M-FIC
EVA_Neuemission,Inv.Fonds,Verkauf,Neuemission_Information,Anlageberatung,Kunde,schriftlich,Beratungsprotokoll_Kunde,non-PRIIP_auf_Blackliste,WA_von_FWW
EVA_Neuemission,Inv.Fonds,aenderung,Neuemission_offen,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,PRIIP_auf_Blackliste,PIB_von_DOTi
EVA_Neuemission,Inv.Fonds,Streichung,Neuemission_geschlossen,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Kunde,UDAL_nicht_verfuegbar,WA_von_FWW
EVA_Direkteinstieg,Zertifikat,Streichung,Sekundaermarkt,Anlageberatung,Kunde,Haustuergeschaeft,Nachtraeglicher_KID-Versand,Normalbetrieb,PIB_von_DOTi
EVA_Direkteinstieg,OIF,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,Bank,telefonisch,Beratungsprotokoll_Kunde,Back_up-Betrieb,KID_von_externem_Hersteller
EVA_Direkteinstieg,Xetra_Gold_ETC,Kauf_(ohne_Beratungsdatum),Neuemission_abgerechnet,Anlageberatung,Kunde,persoenlich,Beratungsprotokoll_Interessent,PRIIP,KID_von_PC_PM
EVA_Direkteinstieg,Optionsschein,aenderung,Neuemission_geschlossen,Anlageberatung,Kunde,schriftlich,Beratungsprotokoll_Potenzial,ohne_KID,KID_von_DOTi
EVA_Direkteinstieg,Unstrukturierte_Anleihe,Verkauf,Neuemission_geschlossen,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,WKN_nicht_vorhanden,WA_von_FWW
EVA_Direkteinstieg,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,WKN_inaktiv/geloescht,PIB_von_C&M-FIC
EVA_Direkteinstieg,Inv.Fonds,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,defektes_Dokument,PIB_von_PC_PM
EVA_Direkteinstieg,strukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,Kunde,schriftlich,Beratungsprotokoll_Kunde,Virus,PIB_von_DOTi
EVA_Direkteinstieg,OIF,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,non-PRIIP_auf_Blackliste,PIB_von_DOTi
EVA_Direkteinstieg,OIF,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,PRIIP_auf_Blackliste,PIB_von_C&M-FIC
EVA_Direkteinstieg,OIF,Kauf(Beratungsdatum_gueltig),Sekundaermarkt,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,UDAL_nicht_verfuegbar,PIB_von_C&M-FIC
HOST_T19000,Aktie,Storno,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
HOST_T19001,Unstrukturierte_Anleihe,Berichtigungsauftrag,Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
HOST_T19901,strukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_PC_PM
HOST_T19750,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T28900,OIF,Verkauf,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_PC_PM
Onlinebanking_PC,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,KID_von_externem_Hersteller
Onlinebanking_PC,OIF,Streichung,Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,PRIIP,KID_von_DOTi
Onlinebanking_PC,Zertifikat,Kauf(Beratungsdatum_gueltig),Sekundaermarkt,beratungsfreies_Geschaeft,n/a,n/a,n/a,ohne_KID,WA_von_FWW
Onlinebanking_PC,strukturierte_Anleihe,Kauf_(ohne_Beratungsdatum),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,WKN_nicht_vorhanden,PIB_von_DOTi
Onlinebanking_PC,Inv.Fonds,Verkauf,Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,WKN_inaktiv/geloescht,PIB_von_C&M-FIC
Onlinebanking_PC,Aktie,aenderung,Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,defektes_Dokument,KID_von_PC_PM
Onlinebanking_PC,Unstrukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Virus,KID_von_DOTi
Onlinebanking_PC,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,non-PRIIP_auf_Blackliste,PIB_von_C&M-FIC
Onlinebanking_PC,Zertifikat,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,PRIIP_auf_Blackliste,KID_von_PC_PM
Onlinebanking_PC,Zertifikat,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,UDAL_nicht_verfuegbar,PIB_von_PC_PM
Onlinebanking_PC,EMISID,aenderung,Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,KID_von_DOTi
Onlinebanking_MSB,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,PIB_von_PC_PM
Onlinebanking_MSB,Zertifikat,Kauf_(ohne_Beratungsdatum),Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,PRIIP,WA_von_FWW
Onlinebanking_MSB,OIF,Verkauf,Sekundaermarkt,beratungsfreies_Geschaeft,n/a,n/a,n/a,ohne_KID,KID_von_externem_Hersteller
Onlinebanking_MSB,Inv.Fonds,aenderung,Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,WKN_nicht_vorhanden,PIB_von_DOTi
Onlinebanking_MSB,strukturierte_Anleihe,Streichung,Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,WKN_inaktiv/geloescht,PIB_von_C&M-FIC
Onlinebanking_MSB,Unstrukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,defektes_Dokument,WA_von_FWW
Onlinebanking_MSB,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Virus,KID_von_PC_PM
Onlinebanking_MSB,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,non-PRIIP_auf_Blackliste,PIB_von_PC_PM
Onlinebanking_MSB,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,PRIIP_auf_Blackliste,KID_von_DOTi
Infobroker,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,KID_von_PC_PM
Infobroker,Unstrukturierte_Anleihe,n/a,n/a,n/a,n/a,n/a,n/a,PRIIP,PIB_von_PC_PM
Infobroker,OIF,n/a,n/a,n/a,n/a,n/a,n/a,WKN_inaktiv/geloescht,KID_von_externem_Hersteller
Infobroker,Zertifikat,n/a,n/a,n/a,n/a,n/a,n/a,defektes_Dokument,PIB_von_DOTi
Infobroker,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,WA_von_FWW
Infobroker,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,PIB_von_C&M-FIC
Infobroker,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,KID_von_DOTi
HOST_T19000,Aktie,Storno,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
Onlinebanking_Neuemission_PC,strukturierte_Anleihe,Verkauf,Neuemission_offen,beratungsfreies_Geschaeft,n/a,n/a,n/a,WKN_nicht_vorhanden,PIB_von_PC_PM
HOST_T19000,EMISID,Storno,Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T19000,EMISID,Storno,Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_PC_PM
HOST_T19001,EMISID,Berichtigungsauftrag,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
EVA_Order,Xetra_Gold_ETC,Streichung,Neuemission_offen,Anlageberatung,Bank,Haustuergeschaeft,Beratungsprotokoll_Kunde,defektes_Dokument,KID_von_externem_Hersteller
HOST_T19000,Aktie,Storno,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
HOST_T19000,Aktie,Storno,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_PC_PM
HOST_T19000,Aktie,Storno,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_DOTi
HOST_T19001,Unstrukturierte_Anleihe,Berichtigungsauftrag,Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T19001,Unstrukturierte_Anleihe,Berichtigungsauftrag,Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,PIB_von_PC_PM
HOST_T19001,Unstrukturierte_Anleihe,Berichtigungsauftrag,Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,KID_von_PC_PM
HOST_T19001,Unstrukturierte_Anleihe,Berichtigungsauftrag,Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,KID_von_DOTi
HOST_T19001,Unstrukturierte_Anleihe,Berichtigungsauftrag,Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
Onlinebanking_Neuemission_PC,Zertifikat,Kauf(Beratungsdatum_gueltig),Neuemission_offen,beratungsfreies_Geschaeft,n/a,n/a,n/a,non-PRIIP_auf_Blackliste,KID_von_PC_PM
EVA_Anlageberatung,Unstrukturierte_Anleihe,Kauf_(ohne_Beratungsdatum),Neuemission_offen,beratungsfreies_Geschaeft,Bank,persoenlich,Beratungsprotokoll_Interessent,Back_up-Betrieb,PIB_von_C&M-FIC
EVA_Anlageberatung,Unstrukturierte_Anleihe,Kauf_(ohne_Beratungsdatum),Neuemission_offen,beratungsfreies_Geschaeft,Bank,persoenlich,Beratungsprotokoll_Interessent,Back_up-Betrieb,KID_von_DOTi
HOST_T19901,strukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T19901,strukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
HOST_T19901,strukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
HOST_T19901,strukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_PC_PM
HOST_T19901,strukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_DOTi
HOST_T19901,strukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
HOST_T19750,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
HOST_T19750,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
HOST_T19750,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_PC_PM
HOST_T19750,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_PC_PM
HOST_T19750,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_DOTi
HOST_T19750,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
HOST_T28900,OIF,Verkauf,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T28900,OIF,Verkauf,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
HOST_T28900,OIF,Verkauf,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
HOST_T28900,OIF,Verkauf,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_PC_PM
HOST_T28900,OIF,Verkauf,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_DOTi
HOST_T28900,OIF,Verkauf,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
Onlinebanking_Neuemission_PC,EMISID,Streichung,Neuemission_offen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,WA_von_FWW
Onlinebanking_Neuemission_PC,EMISID,Streichung,Neuemission_offen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,PIB_von_DOTi
Onlinebanking_Neuemission_PC,EMISID,Streichung,Neuemission_offen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,PIB_von_C&M-FIC
Onlinebanking_Neuemission_PC,EMISID,Streichung,Neuemission_offen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,KID_von_DOTi
Onlinebanking_Neuemission_PC,EMISID,Streichung,Neuemission_offen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,KID_von_externem_Hersteller
Onlinebanking_Neuemission_MSB,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,WA_von_FWW
Onlinebanking_Neuemission_MSB,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,PIB_von_DOTi
Onlinebanking_Neuemission_MSB,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,PIB_von_C&M-FIC
Onlinebanking_Neuemission_MSB,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,PIB_von_PC_PM
Onlinebanking_Neuemission_MSB,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,KID_von_PC_PM
Onlinebanking_Neuemission_MSB,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,KID_von_DOTi
Onlinebanking_Neuemission_MSB,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,KID_von_externem_Hersteller
Onlinebanking_Tablet_PC,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,WA_von_FWW
Onlinebanking_Tablet_PC,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,PIB_von_DOTi
Onlinebanking_Tablet_PC,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,PIB_von_C&M-FIC
Onlinebanking_Tablet_PC,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,PIB_von_PC_PM
Onlinebanking_Tablet_PC,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,KID_von_PC_PM
Onlinebanking_Tablet_PC,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,KID_von_DOTi
Onlinebanking_Tablet_PC,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,KID_von_externem_Hersteller
commerzbank_de_pib,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,WA_von_FWW
commerzbank_de_pib,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,PIB_von_DOTi
commerzbank_de_pib,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,PIB_von_C&M-FIC
commerzbank_de_pib,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,PIB_von_PC_PM
commerzbank_de_pib,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,KID_von_PC_PM
commerzbank_de_pib,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,KID_von_DOTi
commerzbank_de_pib,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,Normalbetrieb,KID_von_externem_Hersteller
Onlinebanking_MSB,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,UDAL_nicht_verfuegbar,WA_von_FWW
Onlinebanking_MSB,Zertifikat,Streichung,Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,PIB_von_DOTi
Onlinebanking_Neuemission_PC,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,PRIIP,PIB_von_PC_PM
Onlinebanking_Neuemission_PC,Unstrukturierte_Anleihe,Kauf_(ohne_Beratungsdatum),Neuemission_offen,beratungsfreies_Geschaeft,n/a,n/a,n/a,ohne_KID,KID_von_PC_PM
Onlinebanking_Neuemission_PC,Inv.Fonds,aenderung,Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,WKN_inaktiv/geloescht,KID_von_externem_Hersteller
Onlinebanking_Neuemission_PC,OIF,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,defektes_Dokument,WA_von_FWW
Onlinebanking_Neuemission_PC,Zertifikat,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,Virus,PIB_von_DOTi
Onlinebanking_Neuemission_PC,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,PRIIP_auf_Blackliste,PIB_von_PC_PM
Onlinebanking_Neuemission_PC,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,UDAL_nicht_verfuegbar,KID_von_PC_PM
Onlinebanking_Neuemission_PC,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_offen,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,KID_von_DOTi
Onlinebanking_Neuemission_MSB,EMISID,Kauf_(ohne_Beratungsdatum),Neuemission_offen,beratungsfreies_Geschaeft,n/a,n/a,n/a,PRIIP,WA_von_FWW
Onlinebanking_Neuemission_MSB,Unstrukturierte_Anleihe,Verkauf,Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,ohne_KID,PIB_von_DOTi
Onlinebanking_Neuemission_MSB,strukturierte_Anleihe,aenderung,Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,WKN_nicht_vorhanden,PIB_von_C&M-FIC
Onlinebanking_Neuemission_MSB,Inv.Fonds,Streichung,Sekundaermarkt,beratungsfreies_Geschaeft,n/a,n/a,n/a,WKN_inaktiv/geloescht,PIB_von_PC_PM
Onlinebanking_Neuemission_MSB,OIF,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,defektes_Dokument,KID_von_PC_PM
Onlinebanking_Neuemission_MSB,Zertifikat,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,Virus,KID_von_DOTi
Onlinebanking_Neuemission_MSB,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,non-PRIIP_auf_Blackliste,KID_von_externem_Hersteller
Onlinebanking_Neuemission_MSB,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,PRIIP_auf_Blackliste,WA_von_FWW
Onlinebanking_Neuemission_MSB,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,UDAL_nicht_verfuegbar,PIB_von_DOTi
Onlinebanking_Neuemission_MSB,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
Onlinebanking_Tablet_PC,Unstrukturierte_Anleihe,Kauf_(ohne_Beratungsdatum),Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,PRIIP,KID_von_PC_PM
Onlinebanking_Tablet_PC,EMISID,Verkauf,Sekundaermarkt,beratungsfreies_Geschaeft,n/a,n/a,n/a,ohne_KID,KID_von_DOTi
Onlinebanking_Tablet_PC,strukturierte_Anleihe,aenderung,Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,WKN_nicht_vorhanden,KID_von_externem_Hersteller
Onlinebanking_Tablet_PC,Inv.Fonds,Streichung,Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,WKN_inaktiv/geloescht,WA_von_FWW
Onlinebanking_Tablet_PC,OIF,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,defektes_Dokument,PIB_von_DOTi
Onlinebanking_Tablet_PC,Zertifikat,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,Virus,PIB_von_C&M-FIC
Onlinebanking_Tablet_PC,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,non-PRIIP_auf_Blackliste,PIB_von_PC_PM
Onlinebanking_Tablet_PC,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,PRIIP_auf_Blackliste,KID_von_PC_PM
Onlinebanking_Tablet_PC,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,UDAL_nicht_verfuegbar,KID_von_DOTi
Onlinebanking_Tablet_PC,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
Infobroker,strukturierte_Anleihe,n/a,n/a,n/a,n/a,n/a,n/a,ohne_KID,PIB_von_C&M-FIC
Infobroker,Inv.Fonds,n/a,n/a,n/a,n/a,n/a,n/a,WKN_nicht_vorhanden,PIB_von_PC_PM
Infobroker,Optionsschein,n/a,n/a,n/a,n/a,n/a,n/a,Virus,KID_von_externem_Hersteller
Infobroker,Xetra_Gold_ETC,n/a,n/a,n/a,n/a,n/a,n/a,non-PRIIP_auf_Blackliste,WA_von_FWW
Infobroker,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,PRIIP_auf_Blackliste,PIB_von_DOTi
Infobroker,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,UDAL_nicht_verfuegbar,PIB_von_C&M-FIC
Infobroker,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,n/a,PIB_von_PC_PM
commerzbank_de_pib,Unstrukturierte_Anleihe,n/a,n/a,n/a,n/a,n/a,n/a,PRIIP,KID_von_DOTi
commerzbank_de_pib,strukturierte_Anleihe,n/a,n/a,n/a,n/a,n/a,n/a,ohne_KID,KID_von_externem_Hersteller
commerzbank_de_pib,Inv.Fonds,n/a,n/a,n/a,n/a,n/a,n/a,WKN_nicht_vorhanden,WA_von_FWW
commerzbank_de_pib,OIF,n/a,n/a,n/a,n/a,n/a,n/a,WKN_inaktiv/geloescht,PIB_von_DOTi
commerzbank_de_pib,Zertifikat,n/a,n/a,n/a,n/a,n/a,n/a,defektes_Dokument,PIB_von_C&M-FIC
commerzbank_de_pib,Optionsschein,n/a,n/a,n/a,n/a,n/a,n/a,Virus,PIB_von_PC_PM
commerzbank_de_pib,Xetra_Gold_ETC,n/a,n/a,n/a,n/a,n/a,n/a,non-PRIIP_auf_Blackliste,KID_von_PC_PM
commerzbank_de_pib,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,PRIIP_auf_Blackliste,KID_von_DOTi
commerzbank_de_pib,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,UDAL_nicht_verfuegbar,KID_von_externem_Hersteller
commerzbank_de_pib,Aktie,n/a,n/a,n/a,n/a,n/a,n/a,n/a,WA_von_FWW
EVA_Sparplan,EMISID,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,Kunde,schriftlich,Beratungsprotokoll_Interessent,Back_up-Betrieb,PIB_von_DOTi
EVA_Neuemission,EMISID,Kauf(Beratungsdatum_gueltig),Neuemission_abgerechnet,Anlageberatung,Kunde,telefonisch,Nachtraeglicher_KID-Versand,WKN_nicht_vorhanden,PIB_von_C&M-FIC
EVA_Direkteinstieg,EMISID,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,WKN_inaktiv/geloescht,PIB_von_PC_PM
HOST_T19000,Unstrukturierte_Anleihe,Kauf(Beratungsdatum_gueltig),Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,KID_von_PC_PM
HOST_T19000,strukturierte_Anleihe,Berichtigungsauftrag,Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_DOTi
HOST_T19000,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
HOST_T19000,OIF,Verkauf,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T19000,Zertifikat,aenderung,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
HOST_T19000,Optionsschein,Streichung,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
HOST_T19000,Xetra_Gold_ETC,Storno,Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,PIB_von_PC_PM
HOST_T19001,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_DOTi
HOST_T19001,strukturierte_Anleihe,Storno,Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
HOST_T19001,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T19001,OIF,Verkauf,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
HOST_T19001,Zertifikat,aenderung,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
HOST_T19001,Optionsschein,Streichung,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_PC_PM
HOST_T19001,Xetra_Gold_ETC,Berichtigungsauftrag,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_PC_PM
HOST_T19901,Aktie,Berichtigungsauftrag,Neuemission_Information,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
HOST_T19901,Unstrukturierte_Anleihe,Storno,Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T19901,Inv.Fonds,Kauf_(ohne_Beratungsdatum),Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
HOST_T19901,OIF,Verkauf,Neuemission_abgerechnet,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
HOST_T19901,Zertifikat,aenderung,Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_PC_PM
HOST_T19901,Optionsschein,Streichung,Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_PC_PM
HOST_T19901,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_DOTi
HOST_T19901,EMISID,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
HOST_T19750,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T19750,Unstrukturierte_Anleihe,Verkauf,Neuemission_abgerechnet,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
HOST_T19750,strukturierte_Anleihe,aenderung,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
HOST_T19750,OIF,Storno,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_PC_PM
HOST_T19750,Zertifikat,Berichtigungsauftrag,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_PC_PM
HOST_T19750,Optionsschein,Streichung,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_DOTi
HOST_T19750,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
HOST_T19750,EMISID,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T28900,Aktie,Kauf(Beratungsdatum_gueltig),Neuemission_abgerechnet,beratungsfreies_Geschaeft,n/a,n/a,n/a,n/a,PIB_von_DOTi
HOST_T28900,Unstrukturierte_Anleihe,Kauf_(ohne_Beratungsdatum),Sekundaermarkt,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
HOST_T28900,strukturierte_Anleihe,aenderung,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_PC_PM
HOST_T28900,Inv.Fonds,Storno,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_PC_PM
HOST_T28900,Zertifikat,Streichung,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_DOTi
HOST_T28900,Optionsschein,Berichtigungsauftrag,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
HOST_T28900,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T28900,EMISID,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
Onlinebanking_MSB,EMISID,Kauf(Beratungsdatum_gueltig),Neuemission_geschlossen,beratungsfreies_Geschaeft,n/a,n/a,n/a,defektes_Dokument,PIB_von_C&M-FIC
EVA_Anlageberatung,Optionsschein,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,Kunde,Haustuergeschaeft,Beratungsprotokoll_Kunde,Back_up-Betrieb,PIB_von_PC_PM
EVA_Anlageberatung,Xetra_Gold_ETC,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,Back_up-Betrieb,KID_von_PC_PM
EVA_Anlageberatung,EMISID,Kauf(Beratungsdatum_gueltig),Neuemission_Information,Anlageberatung,Kunde,telefonisch,Beratungsprotokoll_Kunde,Virus,KID_von_DOTi
HOST_T19000,Inv.Fonds,Berichtigungsauftrag,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,KID_von_externem_Hersteller
HOST_T19000,OIF,Berichtigungsauftrag,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,WA_von_FWW
HOST_T19000,Zertifikat,Storno,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_DOTi
HOST_T19000,Optionsschein,Storno,Neuemission_geschlossen,Anlageberatung,n/a,n/a,n/a,n/a,PIB_von_C&M-FIC
Onlinebanking_Neuemission_PC,Aktie,Kauf(Beratungsdatum_gueltig),Sekundaermarkt,beratungsfreies_Geschaeft,n/a,n/a,n/a,Normalbetrieb,PIB_von_PC_PM

制約のないモデルだとPICTは目を瞠るような速さでテストの生成を行う。因子数100、各因子の水準数2の場合、文字通り一瞬で生成が終わる(テストケース数16)。JCUnitは30秒ほどかかりテストケース数は18となるので、さすが老舗は違うなと舌を巻いたのだが、制約の取り扱いについてはJCUnitの方が少しだけ優れていると言えるようになったようだ。

IEEE International Conference on Software Testing 2017でJCUnitの発表をすることになりました。

表題の通りです。スケジュールはこちら。

ICST 2017 | Schedule

東京で開催されるカンファレンスなのに、日本人の発表者があまり見当たらないのは残念なことです。 2/13まではEarly Birdで少しだけお安くお申し込みできます。

ICST 2017 | Registration

キーノ−トスピーチ等は同時通訳もつくそうです。

A new IPO-G based algorithm that supports constraint handling

Background

A couple of weeks ago, when I was writing a jcunit demo that tests geophile[0] library, I noticed that its old constraint handling doesn't work well. Since the behavior looked hard to fix without hurting compatibility and it would take a bit time, I decided to implement another algorithm which I came up with.

Introduction

I'm calling this new algorithm IPO-gc, named after well-known one IPO-G. Before getting into my own, I would shortly touch up on its ancestor.

IPO-G[7], which is a basis of my new algorithm IPO-gc, is one of best known combinatorial test suite generation algorithms where it grows up an initial covering array, generated by calculating Cartesian products of first t(= desired strength) factors, horizontally and vertically until all the desired tuples are covered.

Following is a pseudo code to illustrate the algorithm.[7]

    Algorithm: IPOG-Test (int t , ParameterSet ps ) {
      1.  initialize test set ts to be an empty set
      2.  denote the parameters in ps , in an arbitrary order, as P1 , P2, ...,
          and Pn
      3.  add into test set ts a test for each combination of values of the first
          t parameters
      4.  for (int i = t + 1 ; i ≤ n ; i ++ ){
      5.     let π be the set of t -way combinations of values involving parameter
             Pi and t -1 parameters among the first i – 1 parameters
      6.     // horizontal extension for parameter Pi
      7.     for (each test τ = (v 1 , v 2 , ..., v i-1 ) in test set ts ) {
      8.         choose a value vi of Pi and replace τ with τ’ = (v 1 , v 2 ,
                 ..., vi-1 , vi ) so that τ’ covers the most number of
                 combinations of values in π
      9.         remove from π the combinations of values covered by τ’
      10.    }
      11.    // vertical extension for parameter P i
      12.    for (each combination σ in set π ) {
      13.      if (there exists a test that already covers σ ) {
      14.          remove σ from π
      15.      } else {
      16.          change an existing test, if possible, or otherwise add a new test
                   to cover σ and remove it from π
      17.      }
      18.    }
      19. }
      20. return ts;
      }

This algorithm consists of 4 most core elements, which are

  1. *ts* = Create a initial covering array from first *t* factors.
  2. *π*  = Compute partial tuples to be covered by an iteration

  foreach *factor* not in first *t* factors:
     3. hg: Grow *ts* horizontally by adding a *factor* to the right of given 
        one so that partial tuples in *π* as many as possible. 
        covered tuples by this step will be removed from *π*.

       while *π* is not empty
       4. vg: Grow *ts* vertically by adding a new full tuple that covers partial 
          tuples in π as many as possible. If there is already a tuple in *ts* that
          can cover another partial tuple in *π*, it will be modified to cover the
          partial one instead of adding a new one to *ts*.

where "a full tuple" means a tuple which has all the first i attributes and "a partial tuple" means a tuple in π, only has t attributes.

IPO-gc algorithm

IPO-gc's core idea is simple.

  • Enhance definitions of hg and vg operation so that it can create a new covering array from 2 partial covering arrays, not only from 1 covering array and 1 factor.
  • Consider a simple factor a special form of covering arrays.
  • Group given constraints into smaller chunks, each of which reachable from one constraint to another connected by shared factors.
  • For each group of constraints, create covering array from factors referenced by the constraints in it. In the current implementation it is a brute force algorithm, but it can be any algorithm such as PICT, IPOC, or other AETG based algorithms.

Assumptions behind the last item is following,

  • Generally speaking, constraints defined over a test space can be grouped into some smaller groups each of which contains only small amount of factors.

In my limited experience, this is true for automatic test suite generation for FSM's by JCUnit[1]. And actually it would be considered that creating a set of constraints that relate to many factors directly and indirectly isn't a good idea[8]

Definitions

Before discussing further details, I want to define following several terms.

  • FS Factors.
  • CS Constraint sets.
  • t Desired strength of a generated test suite.
  • ts Generated test suite. To which the algorithm adds generated tests by growing up horizontally and vertically.
  • τ A (current) element in ts.
  • π Partial tuples that should be covered in a current iteration. A tuple in this set has values from FS (simple factors) only. Not from GFS because this set is used to evaluate how good a next choice is and it should be done based on how many possible partial tuples generated from FS not from GFS.
  • GFS Grouped factors. A grouped factors consists of one or more factors. And has zero or more constraints each of which uses one or more factors in it, but none of the constraints uses any factors outside the grouped factor. A grouped factor is one form of a "covering array" whose characteristics will be discussed later.
  • GFS.length Total number of grouped factors in GFS.
  • GFS[ i] ith element of GFS.
  • GFS[ i].subfactors Simple factors that belong to GFS[ i].
  • GFS[ i].constraints Constraints that belong to GFS[ i].
  • GFS[ i].levels Levels of GSF[ i], which are tuples that consist of values from GFS[ i].subfactors.
  • A grouped factor: If a set of factors FS, strength t, and constraints CS over them are given, we can define a grouped factor GF(FS, CS, t) as follows.

    • It is a set of full-tuples all of FS.
    • All the possible tuples of strength t over FS are covered by it.
    • None of CA violates any of CS.
    • For simplicity, if an element is removed from it and the conditions above are still met, it will be removed.

One thing to be noted here is once a covering array is computed for a given factors and their constraints, we are always able to compute all the possible partial tuples of strength t by just scanning elements in it. You do not need to worry about what constraints are given.

Ideas

Figures to illustrate the ideas of IPO-gc are as follows

In IPO-G, horizontal growth hg is defined as an operation between, ts, Pi (a conventional factor), t(desired strength), and π (tuples to be covered in this iteration).

   IPO-G
    
        ts                                ts'
         P0 P1...Pi-1    Pi                P0 P1...Pi-1 Pi
        +---------+     +---+             +---------------+
        |         |     |v0 |             |               |
        |         |     |v1 |             |               |
        |         |     |.  |             |               |
   hg(  |τ        |  ,  |.  |  , t, π) => |               |
        |         |     |.  |             |               |
        |         |     |   |             |               |
        |         |     |   |             |               |
        +---------+     +---+             +---------------+
    

π is calculated from a set of all possible tuples of strength t in ts and all the levels of Pi just by Cartesian product.

This will look like following diagram in IPO-gc.

   IPO-gc
    
        ts                                            ts'
         GF[0] GF[1]...GF[i-1]    GF[i]               GF[0] GF[1]...GF[i]
        +---------------------+     +---+             +---------------+
        |                     |     |v0 |             |               |
        |                     |     |v1 |             |               |
        |                     |     |.  |             |               |
   hg(  |τ                    |  ,  |.  |  , t, π) => |τ'             |
        |                     |     |.  |             |               |
        |                     |     |   |             |               |
                              |     |   |             |               |
        +---------------------+     +---+             +---------------+
    

GF[ i] is a grouped factor to be added to ts. π is calculated by following procedure.

  • Pick up all the possible partial tuples of strength x (smaller than t)from ts. Call it Left.
  • Pick up all the possible partial tuples of strength y (x + y = t)from GF[i]. Call it Right.
  • Calculate cartesian product between Left and Right. => π

When expanding ts to ts', values for GF[i] of τ' will be chosen to cover as many tuples in π as possible.

In vg (vertical growth) operation, there's a not a big difference than in hg. Add a new test or modify existing test to cover a new tuple. But when we add or modify a tuple, attributes that belong to the same grouped factor need to be updated at once. Not one by one. Otherwise, we may create a test case that violates some of given constraints.

To build a grouped factor, we group constraints first by original factors involved by them.

Suppose that factors Pi ( 0 <= i <= 7) and constraints C1, C2, C3, and C4 are given. And factors involved in each constraint are described as in following diagram.

       P1    P2    P3    P4   P5    P6    P7
   C1  |-----|           |              
   C2        |           |
   C3               |         |-----|
   C4                                     |

In this case, IPO-gc will build 3 grouped factors

  • GF[ 0] = ({P1, P2, P4}, {C1, C2})
  • GF[ 1] = ({P3, P5, P6}, {C3})
  • GF[ 2] = ({P7}, {C4})

Note: A simple factor can be considered a grouped factor.

Algorithm

In short, it is an algorithm that transforms given factors and constraints into grouped factors, which are actually a set of covering arrays under related constraints, of desired strength t. And then do enhanced IPO algorithm, which can handle horizontal growth between current ts (test suite) and a grouped factor.

   IPO-gc algorithm (summarized version)
     Input:
     - FS: Factor set
     - CS: Constraint set
     - t:  strength
     Output:
     - ts <- ca(FS, CS, t)

   1.     GFS[] = transform(FS, CS, t)
   2.     ts    = initial_covering_array(GFS[0], ..., GFS[t-1])
   3.     foreach GF in [GFS[t], GFS[t + 1], ..., GFS[GFS.length - 1]] {
   3.0      i = indexOf(GF in GFS)
   3.1      π = compute_all_possible_partial_tuples([GFS[0], ..., GFS[i-1]], GF)
            // horizontal extention
   3.2      foreach τ in ts[] {
   3.2.1      v = choose_best_level(τ, GF, π)
   3.2.2      update(τ, GF, v)
   3.2.3      remove_subsuples(π, τ)
            }
   3.2.4    // vertical extension for parameter P i
   3.2.5    for (each combination σ in set π ) {
   3.2.6      if (there exists a test that already covers σ ) {
   3.2.7        remove σ from π
            } else {
   3.2.8      change an existing test, if possible, or otherwise add a new test
              to cover σ and remove it from π
            }
          }               

In the step 1, I just stated "transform simple factors into grouped factors". But each grouped factor is a covering array for given factors and constraints and it's very preferable to make it small. In other words, we still need to solve the same problem, "how to create a covering array from given factors under given constraints".

IPO-gc algorithm is not providing a good way to solve it. It is rather a framework to create a bigger covering array from given ones. In current implementation in JCUnit, what I created is just a very naive one. Just check all the possible combinations and exclude ones that violate any of related constraints. And then remove redundant ones (that do not cover new combinations). This approach can become easily impractical when many factors are involved in constraints. But,

  1. Maybe you should revise the test design so that we can make the model simple.
  2. It is easy to use another better algorithm to handle constraints and generate a covering array.

Another thing that needs to be achieved in this step is to group given constraints and factors into some cohorts. If a constraint C1 uses factors A and B, and another constraint C2 uses B and C, those will belong to the same group. And factors A, B, and C will belong to the same grouped factor. Plus C1 and C2 will belong to it, too. Because B is shared by C1 and C2, and this means we need to take all these 3 factors into account at once to check if a given tuple is violating any of them or not.

But C3, that only involves factor D and E, it will belong to another grouped factor, because there is no overlap between factors referenced by C1 and C2.

In step 3.2.1, original version of IPO-G just chooses one level from a simple factor that covers tuples in π the most. But in IPO-gc it is required to choose a set of levels in a manner the set of chosen levels do not violate any given constraints. This can be achieved by choosing a level of a grouped factor rather than choosing levels from individual (sub-)factors.

In step 3.2.8, the intention of the step is also same as the original one, but what we need to do is a bit different. If σ is involved in a constraint, other attributes might be required to be a specific value or in a specific range. In such case, we need to change not only the values directly mentioned in σ but also other (sub-)factors that belong to the same grouped factor.

NOTE: In step 3.1, We are calculating cartesian product of GFS[ 0 ], ..., GFS[i-1], and GF. But we should be able to make the step faster by scanning ts instead of the expensive operation.

   3.1    π = compute_all_possible_partial_tuples(ts, GF)

Because ts should cover all the desired partial tuples at the beginning of an iteration. But we are not getting into further detail of it for now since it is an implementation detail.

Advantages and limitations of this algorithm

  • Easier to make it parallel: Calculating covering arrays is an expensive operation and we can do it concurrently.
  • Easy to implement: I could write this mechanism with in 2 weekds from I came up with it.

Future works

  • Survey: I invented this algorithm but did not conducted any survey to claim it is a novel one. Maybe I'm just re-invented it since it's sort of naive. Also the assumption mentioned in IPO-gc algorithm section, where constraints should not involve so many parameters and therefore brute force algorithm is sufficient, should be surveyed to what extent it is applicable.
  • Use other algorithms to generate a covering array for a grouped factor: It is easy to enhance the implementation to make the algorithm pluggable but right now it is hard-coded because there is only one available algorithm.
  • Evaluation: Let's compare this algorithm with other ones. Sizes of test suites, performance, etc.
  • Synthesize a new level from existing levels instead of just choosing one from a "grouped factor"(covering array under constraints): This will make generated test suite's size smaller.

References

(第四回)JCUnitで2次方程式を解くプログラムをテストする。

はじめに

本ブログでご案内しているように、javacのバグによりJDK1.7.0_79以前でコンパイルエラーが生じることがわかった。 この問題を回避するために、jcunit 0.6.4をリリースしたので、pom.xmlを以下のように更新して欲しい。

    <dependency>
      <groupId>com.github.dakusui</groupId>
      <artifactId>jcunit</artifactId>
      <version>[0.6.4,)</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

(第四回)JCUnitで2次方程式を解くプログラムをテストする。

前回のエントリでは、FL表を作成し、それにもとづいてJCUnitにテストスイートを生成させたところ、109件ものテストができた。 これについてすでに以下のように述べた。

  1. テストケースが多すぎる
  2. 正常系のテストと異常系のテストが混ざっていてレビューしづらい
  3. 同時に複数の制約に違反するテストケースが多い。この種のテストケースは多くの場合あまり意味がない

テストスイートを自動で生成し、かつ自動で実行するとしてもテストケースの数は大きな問題になる。 あまりに数が多すぎれば管理ができない。多くのテストが様々な理由で失敗している場合、失敗している理由を捕捉するのに手間がかかる。 また、適切にテストオラクルを作るのが難しい場合、ひとまず現在の挙動を記録しておき、プログラム修正後(リファクタリング等)の次回実行時に前回との差異を点検することでテストとする場合もあろう。 こうした時に、何千何万というテストケースがあったのでは容量を食い過ぎるという場合もある。 また、私としてはテスト対象の外部的な仕様に基づいてテストを設計・実装・実行するために、つまり結合テストシステムテストの段階で、JCUnitを使いたいのだがそうすると一件のテストの実行時間は短くても500msec、ながければ数分ということがあり得る。この場合、如何に自動で生成・実装・実行されるとしても無駄なテストをする余裕は無い。

そもそも、JCUnitは人間が与えたソフトウェアの仕様にそって、テストを生成・実行するものである。テストが意図通りに生成され、実行されているかは人間によるレビューが必要だろう。 何万件、何十万件もテストを実施してもそのテストが正しいかをレビュー・点検する方法を欠いているのでは、いささか中途半端というものではないか。

正常系と異常系のテストが入り組んでいるのも同じような理由で好ましくない。 設計者は正常系の挙動と異常系の挙動を区別してソフトウェアを設計するもので、これらのテストが交互に現れたのではレビューにならない。

「制約」と「条件」の区別

上述諸点を改善するためにどうする必要があるか?「制約」と「条件」を明確に区別してはどうか?というのがJCUnitのアイデアだ。 前回の記事で紹介した@Conditionは実を言えばもともと単にテストを実行する「条件」を記述するためのものだ。

例えばある「OS」という因子があるとして水準が「Windows」のときと「Linux」や「MaxOSX」の時では異なる事項を確認したい。そういう際に、

  @Test
  @When({ "isWindows" })
  public void openInternetExplorerAndGoToWikipedia$thenWelcomPageIsShown() {
      ...
  }

  @Test
  @When({ "isWindows","isLinux" })
  public void openFirefoxAndGoToWikipedia$thenWelcomPageIsShown() {
      ...
  }

  ...

このように使い分けるためのものだ。

「制約」も「条件」の一種と言えるが、こうした普通の条件とは重要な違いがある。まず、通常、アプリケーションやシステムというものは、異常(環境設定、ユーザ入力、内部状態等)を検出したら、その時点で処理を打ち切り、それ以降の処理(それ以外の制約検査も含め)は行わない。

  1. 一つ以上制約が破られたら、そのテストケースがカバーしようとしている値の組み合わせはそれ以降の正常な処理によって使われない可能性がある。(組み合わせ網羅率の低下)
  2. 一つのテストケースで複数の制約が同時に破られる(複数の異常が発生する)と、テスト対象システムは最初に検出した異常に反応して処理を打ち切る。したがってこれら複数の異常のうち、(たまたま)システムが検出した異常処理のみが行われる。
  3. 制約違反を検出して、異常を報告するのはそれ自体システムが備えるべき機能であって、正常機能とは独立して検査する必要がある。

組み合わせテストは、処理が最後まで正常に行われるべき場合に、システムが正しく動作するかを効率的に検証するためのテクニックである。 テスト対象システムの動作条件がWindows 7以降であるときに、以下のようなテストスイートが生成されたとしよう。

OS Browser Site's Language
Windows XP Safari English
Windows 7 Firefox Japanese
Linux Chromium English

対象システムは、稼働OSがWindows XPであることを検出した時点で、異常を報告して処理を終了する。しかしブラウザがSafariで言語がEnglishの組み合わせが別のテストケースによって網羅されている保証はどこにもない。むしろ、良い組み合わせテスト生成ツールほど、テストスイートを小さくまとめるために網羅しなくなる可能性が高いと言える。

制約違反を適切に報告できるかもシステムの機能としてテストする場合、それはまとめてではなく一個一個行う必要があることは上に述べたとおりである。10個独立な制約があったら、これらのすべてについて、1個、そして1個だけに違反するようなテストケースをそれぞれ作成する必要があることになる。

経験を積んだソフトウェアエンジニアであれば、このあたりは本能的に知っている。例えば「処理対象にして良い文字が入力できるか」をテストする場合には、テストデータに「際どいけど正常な文字」を全部いっぺんに使うように試みる。

    AZaz09ー。、.,能あをんアヲン?

こうすることによって、「処理できるべき文字がすべて処理できるか」については一度にテストが行える。 しかし、「処理を中断するべき文字がすべて拒否されるか」については一個一個テストを行う必要がある。例えば、\が拒否されるかと、"が拒否されるかは別のことなので、両方を一つのテストデータに含めてしまうとどちらによってシステムが処理を拒否したのかがわからなくなってしまう。拒否された場合のメッセージが適正かなどはそれでも検査できるにしても、不当な文字を適正に拒絶できているかは別のことである。

JCUnitでの解決方法

JCUnitでは以下のようにしてこうした問題に対処することになる。 まず、前回のコード例は以下の場所にある。

github.com

@RunWith(JCUnit.class)
public class QuadraticEquationTest {
  @FactorField(intLevels = {1, 0, -1, 100, 101, -100, -101, Integer.MAX_VALUE, Integer.MIN_VALUE})
  ...

  /**
   * 制約1: ```a```が0の場合、例外が送出される。(**問題4:a==0**)
   */
  @Condition
  public boolean aIsNonZero() {
    return this.a != 0;
  }
  ...

行うべき変更は

  1. 「制約」を前節で述べた方針にそって特別扱いするようJCUnitに指示する。
  2. @Conditionで定義していた条件が単なる条件ではなく、特別な取り扱いであることを明示する。

このニつである。 以下が変更後のファイルである。

github.com

@RunWith(JCUnit.class)
@GenerateCoveringArrayWith(checker = @Checker(value = SmartConstraintChecker.class)) // <-- 追加:1. のための変更
public class QuadraticEquationTest {
  @FactorField(intLevels = {1, 0, -1, 100, 101, -100, -101, Integer.MAX_VALUE, Integer.MIN_VALUE})
  public int   a;
  ...


  /**
   * 制約1: ```a```が0の場合、例外が送出される。(**問題4:a==0**)
   */
  @Condition(constraint = true) // <-- 修正:2. のための変更
  public boolean aIsNonZero() {
    return this.a != 0;
  }    ...

これを実行してみよう。

f:id:dakusui:20160410171812p:plain

お気づきのように、今回は34件のテストしか生成されていない。 また、前半(19まで)はすべて成功しているが、後半(20以降)はすべて失敗している。

サイズの減少はa,b,cのそれぞれに制約を設定したため、事実上水準の数が減ったためである。 しかし、複数の因子に関係する制約を加えると、その制約を回避しながら可能な組み合わせをすべて生成するようJCUnitは試みる。これが生じるとテストスイートの大きさはむしろ増すことになる。 今回、二次方程式の判別式に関する制約を(3つの因子すべてに関連する)加えている。これはテストケースの数を増す方向に作用する。が、それ以上に、100よりも大きい水準や-100よりも小さい水準が正常処理に関するテストから取り除いた効果の方が大きかったため全体としてテストケースの数が減ったと見受けられる。

前半と後半で成功・失敗がはっきり別れたのは、上述のコード変更でJCUnitが以下の手順でテストスイートを生成するようになったからである。

  1. 正常テスト(与えられた制約に一つも違反しない)の生成を行う
  2. 異常テスト(与えられた制約の一個だけに違反しているテスト)を生成する。
  3. 水準網羅テスト(正常テストのうち一件を取り出し、1., 2.,でまだ網羅されていない因子の水準を一つずつ試す)を生成する。

今回の実行結果では、20件が成功しているが第三回の例では6件のみ成功していた。正常系についてテストが不足していた可能性がある。

余談

この部分をデフォルトにしてしまえばよいような気がしてきた。

@GenerateCoveringArrayWith(checker = @Checker(value = SmartConstraintChecker.class)) 

読者のご意見をこう次第である。