PICTでEmacsからテストケースを生成する

ペアワイズ法(またはオールペア法)という、パラメータが多いテスト対象のパラメータをいい感じに生成してくれる手法と、その考えをもとにパラメータを生成してくれる[PICT]というコマンドラインツールがあります。

今回はそのPICTをEmacsから使うパッケージを作ってみたよというお話をしましょう。

https://github.com/zonuexe/pict.el

Emacs 29以降を使っていればインストールはめちゃくちゃ簡単。

```el
(package-vc-install
 '(pict :url "git@github.com:zonuexe/pict.el.git"
        :main-file "pict.el"))
```

この式を評価するだけでおしまいです。

PICTコマンドは別にインストールする必要がありますが、macOSユーザーなら `brew install pict` だけで入ります。他のOSのユーザーは各自適当にぐぐってインストールしておいてください。

## ペアワイズ法について

どんなときにペアワイズ法とPICTが役に立つかは、Web上に情報があるので各自お読みください。

https://dev.classmethod.jp/articles/pairwise-testing-with-pict/

https://dev.classmethod.jp/articles/introduction-to-combination-testing-methods/

筆者はテストとか詳しくない… 俺たちはプリキュアで学んで雰囲気でPICTを使っている…

https://qiita.com/greymd/items/ad18aa44d4159067a627

## org-babelを使う

「EmacsでPICTを使う」と言ったのですが、実体としてはorg-modeをインターフェイスとして活用していきましょう。

みなさまはorg-babelをご存じでしょうか。org-modeはMarkdownのような軽量マークアップ言語… ではあるのですが、より多くの豊かな機能がEmacsに統合されているのです。

そのひとつがorg-babel。

https://orgmode.org/worg/org-contrib/babel/intro.html

これも語るべきことは数多くありますが、Markdownのコードブロックのように文章中に埋め込んだものをさくっと実行して、結果を記録したりできます。

要はEmacs上でJupyter Notebookみたいなものが使えると考えるのがいいでしょう。

詳しくは @takeokunn の記事を読めば雰囲気が掴めるかもしれません。

https://www.takeokunn.org/posts/permanent/20240112071806-my_relationship_with_org_mode_in_2023_and_how_navigate_in_2024/

## ob-pictを使う

適当に新しいファイル `pict-sample.org` を作って以下のコードを貼り付けてみましょう。

```shell:pict-sample.org
* Check web pages by operating system, browser and language

#+begin_src pict
  OS: Windows, macOS, Debian, Android, iOS
  Browser: Firefox, Chrome, Safari
  Lang: English, Traditioal Chinese, Simplified Chinese, Japanese, Korean

  if [OS] NOT IN {"macOS", "iOS"} then [Browser] <> "Safari";
  if [OS] = "iOS" then [Browser] = "Safari";
#+end_src
```

ここでは、開発中のWebサービスの動作チェックをする際の「OS × ブラウザ × 言語設定」の組み合わせを絞ることを考えています。

上の部分はテストで組み合わせたいパラメータの種類、下の`if`は「SafariはmacOSとiOSでだけ動作する」「iOSではSafariだけテストすればいい」という制約条件を表しています。

:::note
あくまで組み合わせを考える上での例題みたいなものなので、PICTを使ってテストケースを減らすことが実用的なのかは各自で考えてみてね！
:::

`pict.el`のインストールに成功していれば、構文にいい感じの色がついてるはずです。

![スクリーンショット 2024-12-14 11.51.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/f90cd769-27d0-05ef-4274-66f3ed18f9ba.png)

`#+begin_src` `#+end_src` の部分がコードブロックです。Markdownの `` ``` `` に比べてずいぶんややこしいと思ったでしょう。私も三日前までそう思ってました。

`C-c C-, s` で入力できます。*私はリズムで体に叩き込みました*。
で、このコードブロックにカーソルを合わせて `C-c C-c` を押します。

すると「コードブロックのpictコードをお前のシステムで実行しちゃってええか？」みたいなことを聞かれるので`yes`と答えてやってください。

![スクリーンショット 2024-12-14 11.55.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/8faec48f-9a03-a4c6-e013-623a76a69133.png)

そうすると、PICTコマンドがきちんとセットアップできていれば`:+RESULTS`に続いて以下のようなテーブルが展開されます。

![スクリーンショット 2024-12-14 11.58.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/c428e6a8-494f-710d-66a9-c9cdf8a49dd8.png)

なんか総当たりしなくても、そこそこ網羅できそうだなって組み合わせが出てきましたね！

出力は[org-table](https://orgmode.org/manual/Tables.html)という、org-mode組み込みの機能で表組みをいい感じにサクサクいじれるようになっています。

表を見やすくするために、`C-c ^`でソートしてみましょう。

![3d8f5bfd1149e25205fe46e22e9a655d.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/50cb5c6a-8eca-9d0a-8c91-16d0728dcf4d.gif)

まあわかりやすくなったんじゃないでしょうか。

## 入園料問題を解く

もうちょっと別の例として、テストのパターンを列挙するだけではなく、「この組み合わせのときはこうなる」という結果まで含めて考えてみましょう。

PICTは「デシジョンテーブル(決定表)」を作るための手助けもできます。

https://qiita.com/blue32a/items/add0dfdc24c447a5c15c

https://shiftasia.com/ja/column/%E3%83%87%E3%82%B7%E3%82%B8%E3%83%A7%E3%83%B3%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%EF%BC%88%E6%B1%BA%E5%AE%9A%E8%A1%A8%EF%BC%89%E3%81%A8%E3%81%AF/

次の例題として、JaSST'07 Tokyoで発表されたらしいという、「[三賢者、テストを語る （DTvsCEGvsCFD）](https://www.jasst.jp/archives/jasst07e/pdf/A5.pdf)」から『入園料問題』を考えてみましょう。

![スクリーンショット 2024-12-14 7.23.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/db4649aa-919f-36ea-5a0a-54c5ba3f527f.png)

https://ameblo.jp/pictmaster/entry-11643789582.html

上の図の通りの制約をPICTに起こしてみましょう。

```shell:pict-sample.org
#+begin_src pict
  区分1: 個人, 団体
  区分2: 一般, 小学生, 65歳以上, 6歳未満
  県内在住: Yes, No
  入場料: 1200円, 1000円, 600円, 500円, 無料

  IF [区分1] = "個人" AND [区分2] = "一般" THEN [入場料] = "1200円";
  IF [区分1] = "団体" AND [区分2] = "一般" THEN [入場料] = "1000円";
  IF [区分1] = "個人" AND [区分2] = "小学生" THEN [入場料] = "600円";
  IF [区分1] = "団体" AND [区分2] = "小学生" THEN [入場料] = "500円";
  IF [区分2] = "小学生" AND [県内在住] = "Yes" THEN [入場料] = "無料";
  IF [区分2] = "65歳以上" THEN [入場料] = "無料";
  IF [区分2] = "6歳未満" THEN [入場料] = "無料";
#+end_src
```

すると以下のような表が出力されます。

![スクリーンショット 2024-12-14 7.29.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/4bf91d0e-f9a2-1af3-c348-ff04952dae3d.png)

このまま扱ってもいいのですが、表の縦横を転置するには`:var style="transpose"`を追加します。

```diff
-#+begin_src pict
+#+begin_src pict :var style="transpose"
   区分1: 個人, 団体
```

![スクリーンショット 2024-12-14 7.32.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/7819fe04-69a0-999a-a0ae-175687379a33.png)

さらにこのままでもいいのですが、`:var style="matrix"`にしてみましょう。

![スクリーンショット 2024-12-14 7.33.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/b74a489e-658f-b406-3dd4-bdf700625438.png)

org-tableは`M-[←↓↑→]`で上下左右の行と列をさくさく入れ替えられます。

![7234dd78ff914a8448882c69f3f4c7ce.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/dcc5d34d-aaf9-56ed-4b37-70e27076bb60.gif)

最高！

## まとめ

これらを応用すればパラメタライズドテスト(PHPUnitでいうところのDataProvider)のコード生成まで一気にこなせそうですが、一旦はここまでEmacs上で試行錯誤できるだけでそこそこ満足しています。

現状ほかのツールだとPictMasterというExcelで動くツールはもっと機能が充実していたり、CIT-BACHという別の生成エンジンと組み合わせたりランダムに複数回実行して組み合わせを自動圧縮したり賢くやってくれるみたいです。

もっとやれることはたくさんありそうなので、アイディアがある方はじゃんじゃん提案してくださいね！

[PICT]: https://github.com/microsoft/pict