たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

僕的SystemVerilogメモ

絶望の証

何も見えない

進捗は出ないけど知見が増えたので SystemVerilogが一ミリもわからない人向け

Queueは論理合成できない

module Test();
    byte q[$]; //<--できない
    
    initial begin
        q.push_back(12); //<--できない
    end
endmodule

SystemVerilogのQueueはQueueというより双方向Queueで、C++の感覚で使えるので便利だと思ってわくわくしてたけど現行のQuartus Prime 17.0でも論理合成できない。

同じくわくわくしてた連想配列も論理合成できない

//KeyがbyteでValueがintの連想配列
int dic[byte]; // <--できない

structとenumは論理合成できる

でもstructとenumは論理合成できる。enumは値に名前を付けれて便利 enumはデータ型を指定できる。以下はbyteを指定した例

typedef enum byte {TINO=0,KOKOA=1,RIZE=2} RABBITHOUSE;

enum のあとを省略すると32bit?(たぶんint)になる。
さらに代入を省略すると0からの連番になる、先頭に値を 指定するとそこからの連番になる

//zero=0から始まる連番が順番に割り当てになる
typedef enum {zero,one,two,three,four} num;

SystemVerilogのenumはスコープがない・・・というか全部グローバルに宣言される感じ
なので使うときは名前をそのまま書けば使える

typedef enum byte {TINO=3,KOKOA,RIZE} RABITHOUSE;

initial begin
    $display("Data : ",KOKOA); //<-- Data : 4って出力される
end

名前が一緒だったら既にあるっておこられる

typedef enum {TINO} TINOCHAN;
typedef enum {TINO,KOKOA,RIZE} RABBITHOUSE; //<--既にTINOが定義されてるので死ぬ

structも使える

typedef struct packed {
    byte x;
    byte y;
} Vector2;

Vector2 pos; //<--宣言するときはこう

byte tmp=pos.x; //<--xを読み出したいときはこう

初期化?もC++みたいにかける。

Vector2 pos={8'h01,8'h02}; //<-- xに1 yに2

pragma once

インクルードするときC言語みたいに多重定義すると死ぬ
これを書いてる間にSystemVerilogにはC++みたいにpragma onceを指定すれば1度だけインクルードされて多重定義を防げる

//test.sv
`pragma once
module test();
    dark zone
endmodule

//second.sv
`include "test.sv"
modeule second();
    second dark zone
endmodule

//top.sv
`include "test.sv"
`include "second.sv"
module top();
    天衣無縫
endmodule

test.svpragma onceがないとsecond.svの解析中に多重定義で死亡する

これを見つけるまではifdef-else define -endifでやってた。バカス

2017年11月15日追記

なんかやっぱりpragma once はダメだったのでこれは無かったことに
僕が見たのは幻覚か

一回だけいんくるするならやっぱりifdefを使う

`ifdef REG_ONCE
`else
`define REG_ONCE

module register();
endmodule

`endif

これでめでたく解決!(長くてめんどい)

automatic

SystemVerilogは急に変数を宣言できない。回路はstaticだから
変数宣言するならModuleの先頭かinitialブロックの中でしかできない。Moduleの先頭で宣言するとstaticな変数になる。アクセスはModule内ならどこからでもできる しかしコードを書いてると急に変数を宣言したくなるでしょ
そういう時はautomaticキーワードを使えば動的な変数が定義できる

module test();
    byte abc;//<--これはstatic test内ならどこからでもアクセスできる
    
    initial begin
        byte k; //<--initialの中ではOKでもスコープはこのブロックの中だけ
    end
    
    function void abc();
        byte abc=0; //<--これはダメ
        automatic byte edf=0; //<--これはOK
        edf=123; //なんか処理かく
        automatic byte cc=0; //<--これはダメ、automaticをつかって宣言しても結局スコープの先頭でしかできない        
    endfunction

    //automaticを使わないといけないのはtaskやalwaysも
endmodule

変数の種類

japan.xilinx.comhttps://japan.xilinx.com/support/answers/51327.html
ここが詳しい

書いててたまに不便だと思うのがbyteとかが符号付きなところ
二進数世界なのに符号付とかいみわかんなくない?でもプログラム書くとき的には普通じゃないってなってて板挟み

符号なしの多bit幅型を使うときはlogic[15:0]とかって書けば16bitの型が書けるけどこれはいちいち面倒なのでよく使うのはtypedefすればいいと思う

//16bit型
typedef logic[15:0] d_byte;
//ちなみにunsignedを付ければ符号なしにできる
typedef byte unsigned ubyte;

typedefはmoduleの外とかでもできるから全体的によく使うやつとかは別ファイルにまとめて使うときにincludeすると便利かもね。

interface

よく使うポートとかをまとめられる神機能。しかも論理合成できる よくわかんないだろうから例

interface clk_rst;
    bit clk;
    bit rst;
endinterface

module top(clk_rst c);
        // ↑こんな風にclkとrstをまとめられる
    
    always @(posedge c.clk) begin //<--アクセスはstructを同じようにやる
    
    end
endmodule

fork-joinブロック

taskをforkできる sites.google.com
ここが詳しい

eventforeverと組み合わせるとalwaysの代わりが作れるけどこのfork-joinブロックは論理合成できない残念!

これが役に立つと個人的に思うのはテストベンチでクロックを無限に出したいとき

module TB();
    bit clk,rst,endofrun;
    Test test(.*);
    
    task clkgen();
        while(!endofrun)begin
            #50 clk<=0;
            #50 clk<=1;
        end
    endtask
    
    initial begin
        fork begin
            clkgen();
        join_none
        
        //~~~なんか処理
    end
endmodule

このテクニックはネットのどこかで見たから使ってる。どこだったか忘れたので出典はかけない・・・ごめんなさい

まだ何個かある気がするけど思い出すも書くのも疲れたのでここまで
気が向いたらまた書くかもしれないし、マサカリもお願いしたい

cdx アップデート 2.1

cdxアップデート2.1

ついこないだ2.0を出したんですけど思うところあって2.1つくりました

github.com

Python3での書き直し

殆どのスクリプトをPython3で書き直し、分割しました
これにより動作にPython3が必要になりました
必要なパッケージはビルトインだけです

Fish shellに対応

bashだけでなくfish shellに対応しました

config.fishに以下を書き込めば使えるはずです

. /path/to/cdx/sh/func.fish
CDX_DEFAULT_OPTS=すきなオプション(空可)
CDX_DIR="/path/to/cdx"
CDX_FUZZY_COMMAND=いつもつかってるインタラクティブフィルター

変数の追加

CDX_DIR # cdxのディレクトリ

cdxに必要な環境変数

の三つです

–fuzzyの削除

–fuzzyオプションを削除しました
インタラクティブフィルターを使わない利用は無いと感じたからです

ディレクトリファジー検索文字を変更

(良い日本語が思いつかない)
これまでは

cdx ../**
cdx ~/**

とするとパスを展開してディレクトリのリストをインタラクティブフィルターに渡していましたがこれを

cdx ../--
cdx ~/--

に変更しました
理由は~/**だとカレントのファイルがリストアップされて引数に時に面倒だったからです

helpの書き換え

–fuzzyの削除があったためhelpを書き換えました

雑記

fishの文法がかなりつらくいままでのbashシェルスクリプトではうんともすんとも言いませんでした

例えばカレントにあるディレクトリにfzfを使って移動するとき
bashでは以下のように書けば動作しますが

pushd $(ls -l|grep '^d'|awk '{print $NF}'|fzf)

fishでは同じようにプロセス置換を使って書いても動作しません

pushd (ls -l|grep '^d'|awk '{print $NF}'|fzf)

()の中でfzfのようなツールを起動しても利用することができませんでした

解決策としては

ls -l|grep '^d'|awk '{print $NF}'|fzf|read dist;
pushd $dist

としました(bashではこれは使えませんでした)

本当はスクリプトからコマンドを吐いてプロセス置換とかで実装しようと思っていたのですが、細かな文法の違いや上記のことからめちゃくちゃしんどかったです

お布団

ぬくぬく

バイノーラル録音さいこう

昨日の夜お布団に入りながらある音声作品を聞いていました
お布団が擬人化していろいろする〜といった内容で、キャストも好きな方だったので楽しんで聞けました
僕は右耳が 敏感 なので添い寝パートとかはすごく良かったですね

いつも使っているお布団

一通り聞き終わって感想を一人で思っていたとき、ふと自分の使っているお布団のことも考えました
ぼくがいつも使っているこのお布団はもう20年ほど、つまり物心がつく以前から使い続けています
夏の暑い日も、冬の寒い日も春、秋の花粉症の時期も必ず肌に触れる部分はこのお布団でした

おかげで中の綿はボロボロだし、真っ白だった外側のカバーもなんか色ついてきたし、匂いもとても他人にかがせられるような匂いではないです(僕この全部に安心します)
よく「枕が変わると眠れない」とかいいますが僕の場合は「布団が変わると眠れない」です
大学に進学するとき家を出なければだったのですがその時も持って行ったほどです
だからいつまでも使えるように干したり、綿を詰め替えたりの手入れをきちんとやっていつまでも使えるようにって思っています

何が言いたいか

人間誰しも自分が安心できるサンクチュアリを持っているはずです
ぼくの場合はこのお布団にくるまれている時なわけですが、そのサンクチュアリが擬人化してお話出来たら
買い物いけたら、添い寝できたらどんだけ最高だよと

20年も使われたお布団は一体どんな見た目でどんなしゃべり方をするんだろーなーとか思いながら昨日は寝ました

それだけです

cdコマンドのwrapperを自分のためだけに作った

といってもだいぶ前からあるんだけどね

既存のコマンドをシェルスクリプトで魔改装するの最高

cdのwrapperと言えばenhancdとかfishzがありますね github.com
github.com

実は僕もcdxという同じようなコマンドを作っていて、それは2016年の年末にここでも紹介しました
xztaityozx.hatenablog.com

そもそもcdxは快適にcdしたいと思い、ggる前にシェルスクリプトを書き始めたのが最初でした。このときにちゃんとggってればenhancdに出会えたでしょう
そしてなぜenhancdの存在を知ってもなお自前のものを使っていたのか、それは単なる愛着にすぎません

2016年の段階ではenhancdzcdxが勝る点が無かったんですが、「自分のつくったものだし・・・」と思ってメンテをしてきました
そしてenhancdなどにインスパイアされたりして、いくつか機能を付け、自分なりに使いやすくしたので公開しようと思います(それでもenhancdを上回ることはできないでしょう たぶん)

cdx

logo

(ぼくはこういうデザインが好きです)

github.com

README.mdにもつらい英語と日本語でいろいろ書いていますがここでも

デモ

demo

cdxの主な機能は以下です

  • 移動履歴からcdできる
  • ブックマークからcdできる
  • enhancdのようにfzfpecoを使ってファジーサーチできる
  • ~/.ssh/configを読んでsshログインできる
  • 移動先ディレクトリが無いときは作ってから移動できる

などです。全部あんなこといいなできたらいいなで実装したものです

動作環境

bashシェルスクリプトなのでbashを使っている人向けです
普通のシェルスクリプトならそうではないのですがcdxsourceコマンドでcdを反映するのでzshfishでは使えないと思います・・・
改良したいですね

あとはpecoとか`fzfみたいなインタラクティブフィルターがあるともっといいと思います。

github.com
github.com

インストール

git clone https://github.com/xztaityozx/cdx
cd cdx
./install.sh

使い方

cdx [OPTION] PATH

使い方はcdとほぼ同じです。cdよりはオプションの数が多いです

オプション

cdxではいくつかオプションを用意しています。これによって機能のオンオフができます

Option Action
–help ヘルプを表示します
–fuzzy パスの補完や履歴、ブックマークの取得にfzfpecoのようなファジー検索ツールを使用します。使用するツールはCDX_FUZZY_COMMAND変数へ書き込む必要があります
–cd cdxではデフォルトでpushdコマンドを使うようにしていますがこれをcdに置き換えます
–ls cdした後に自動的にlsコマンドを実行します
-h 履歴からcdします。これには–fuzzyオプションが必要です
-b ブックマークからcdします。これには–fuzzyオプションが必要です
+b カレントディレクトリをブックマークに追加します
-p cdpushdの代わりにpopdを実行します
–automake 移動先が見つからなかったとき自動的にディレクトリを作りcdします
–make 移動先が見つからなかったときにディレクトリを作ってもいいか尋ねます
ssh cdxsshを許可します。これをオンにして~/.ssh/configにあるホスト名を与えるとsshログインを試みます。もし同じ名前のディレクトリがある場合はcdが優先されます

これらのオプションを毎回つけるのは面倒だと思ったのでCDX_DEFAULT_OPTS変数に書いてあるオプションをcdxが自動的にオプションに加えるようにしました(fzfの真似をしました)

例) --ssh --fuzzy --makeを毎回オンにしてfzfを使いたい場合~/.bashrc

CDX_DEFAULT_OPTS="--ssh --fuzzy --make"
CDX_FUZZY_COMMAND="fzf"

と書けばいいです

-hと-bオプション

この二つのオプションはそれぞれ履歴とブックマークからのcdをするコマンドですが、インストールを手順通りにやっていればタブでの補完でもファジーサーチが起動するようになっています
タブ補完ではもう一つ。パスの最後が/**となっているときもタブで。それまでのパス以下をリストアップしファジーサーチが起動します

cdx -h <TAB> #履歴オープン
cdx -b <TAB> #ブックマークオープン
cdx /home/hoge/huga/** <TAB> #~/hoge/huga以下に対してしてファジーサーチが起動

sshオプション

sshオプションを有効にしたときはcdxsshログインができるようになります
例えば~/.ssh/config

Host abc
    HostName xxx.xxx.xxx.xxx
    User user
    Port xx

だとすると

cdx --ssh abc

とすると~/.ssh/configの情報を使ってsshログインを試みます。もし同じ名前のディレクトリがある場合は 移動の方を優先 します。

このオプションの実装はシェルスクリプト内で~/.ssh/config内に登録されたHost名かどうかを確かめた後

ssh [HostName]

を実行しているのです。~/.ssh/configの設定をよくする人は使い勝手は同じに感じることができると思います

実はこのオプションの導入をするかどうかは考えました
ディレクトリを移動するcdxコマンド」を考えたときsshログインまでカバーするのはやりすぎだと思いました。
しかし「移動を簡単にするcdxコマンド」として考えたときsshログインもまた移動なのかなと思いました

とか何とか言ってますが一つにまとめられて個人的には満足しています

終わりに

enhancdはすごいです。next generation を謳っているだけはあります。cdxenhancdほど複雑な実装をしていません。コマンドの本体もcdx.shだけで完結しています。
cdxではcdした後にどこに移動したかが目でよくわかるような表示をしています。これは結構見やすくカッコいいと思っています。ただしターミナルの配色によっては見えづらいかもしれません・・・(cdx_echo.shをいじってください)

カッコいい見た目とかブックマークとかsshしたいという人はぜひ使ってもらえたらと思います

BashとかからLINQ使いたくてShelLINQってコマンド作った

BashとかからLINQ使いたくてShelLINQってコマンド作った

ウヒャァ使いやすい

LINQすき

コマンドラインでの作業をしているとき、シェルスクリプトを書いているときに突然LINQが使いたくなる人に向けて作ったコマンドです
docs.microsoft.com

似たような機能を持つ既存のものとしてはawkdatamashがあります
https://ja.wikipedia.org/wiki/AWKja.wikipedia.org
https://www.gnu.org/software/datamash/www.gnu.org

正直awkで十分にできますがぼくはLINQのが使いたかったのです

ShelLINQ

logo

github.com

インストールのしかた

git cloneしてやるだけです

git clone https://github.com/xztaityozx/shellinq
./shellinq/install.sh

aliasを貼ったり補完スクリプトbashrcに書き込むスクリプトを同梱してるので実行すれば使えるようになります

つかいかた

GitHubのREADME.mdと同じことを書くのでそっち見てもらってもいいです

shellinq [TYPEs] [method query] ... [-o [FORMAT]]

[TYPEs]は入力データの型です。int型の場合は[int]と指定します。指定しない場合はint,double,stringのどれかからShelLINQが予想します。
ls -lコマンドのように複数のカラムがある場合は[type1,type2,...,typeN]というように順番に指定します。これも指定しない場合はShelLINQがすべて予想します。

[method query]はクエリ部分です。methodにはSelectWhereのようにLINQが提供するメソッド名を指定します。Qにはメソッドに与える引数部分を指定します。
例えば

Where (x => x% 2 == 0)

where 'x => x% 2 == 0'

と書きます。
queryではawkのようにカラムの指定に$が使えます。
例えばwhere '$1%2==0'と書けばShelLINQはwhere '_=>_%2==0'と解釈します。
$0を使うとすべてのカラムが指定できます。

[-o [FORMAT]]は出力フォーマットをC#のコードで指定するときに使います。 具体的には処理されたデータを使ったforeachループのブロック部を指定します。
取り出されたアイテムは変数itemに格納されているのでそれを使用するといいでしょう。

Input

% seq 30|shellinq select '$1%2==0' -o 'Console.WriteLine(item?"even":"odd");'

output

odd
even
odd
even
.
.
.

デモ

3の倍数を抽出

seq 30 | shellinq where '$1%3==0'

demo1

3の倍数を抽出して30加算

seq 30 | shellinq where '$1%3==0' select '$1+30'

demo2

ls -lの出力を複数カラム纏めて出力

ls -l | sed '1d' | shellinq select 'new Tuple<string,int>($1,$5)'

demo3

これから

複数カラムを選択する場合が今のところかなり面倒なので何とかしたいと思っています

ls -l | sed '1d' | shellinq select '$1,$5'

ぐらいで書けると楽にできそうです

あとは入力をファイル指定出来るようにもしたいですね

雑記

ここからは雑記です
もともと雑記ですけど・・・

実装

ShelLINQは受け取ったクエリと標準入力を元に C#のソースコードを吐き出してmcsでビルド しています
なので処理の実態部分はC#です
それゆえにawk比べると速度はあまり出ません

ソースコードの生成には主に置換を使っています
例えば$1は内部クラスのメンバ変数Item1に置換されます
クエリ内に$1が表れるとItem1に置換されメソッドチェーンに連結されます
なので複雑なコードには対処できないと思います・・・(スイマセン)

C#であるためデータのに注意する必要があります
デモ3のようにstringintと書いてやらないとエラーで死にます
この辺は今後の改良で対処したいと思っています

出力について

上記まででわかる通り置換によってソースコードを作っているため期待通りの出力が得られないことがあると思います

例えばGroupByメソッドを使う場合です
img1

期待しているのと違う出力のほうが出がちです
Selectなどでうまく書いてやる必要がありますがここはLINQの知識が必要なので頑張らないとダメです
この辺の解決のために-oオプションを付けていますがstring.Joinなどで結合するようにすることを予定しています

まとめ

正直LINQが書けて超楽しいです
やることはawkで済む場合のことが多いですけど
良かったらみんなさんも使ってみてください

それではいいShelLINQライフを!

うんこがSL走らせるらしい

夜はさっむい

一年中こんな気候ならいいのに

なんかメールをのぞいたらはてなブログ

そろそろ更新しませんかだってさ
なので最近アツいシェル芸で遊んだお話を書きます

難読化シェル芸という闇

raintrees.net

シェル芸界隈に少し前に現れた究極のジャンル 難読化シェル芸
ここ最近はシェル芸で遊ぶときはこの難読化でわくわくしてます

難読化シェル芸ちょっと研究した

難読化を発見したkanata氏に感銘を受けたのでぼくも少しやってみました

その結果 日本語base64難読化シェル芸 とかいう闇を一つ深めたものを生んでしまいました

これをLT大会で話しました
その時のスライドがこれ~~
www.slideshare.net

詳細はスライドに書いてあるんですがまぁここでも

日本語base64難読化シェル芸とは

基本

日本語をbase64エンコードすることで目的のコマンドを得る手法
例えば「あ」をbase64エンコードすると 44GCCg== が得られる

$ echo "" | base64
44GCCg==

またこれを

$ echo "" | base64 | sed -E 's@(44|g==)@@g'|awk '{print tolower($1)}'
gcc

こうすることでgccコマンドが得られる うーんこれはなかなか

これは実はある法則がある
例えば「ら」「り」「る」をbase64に通すとそれぞれ 44KJ44KK44KL が得られる
つまり4文字目がアルファベット順になってる(実際は3文字目もだけど)

これを使ってコマンド列をレッツメイク

$ echo "そずねぞ" | base64 | grep -o '[a-z]' | xargs | sed 's@ @@g'
date

やったー!

他にもありまぁす

base64を複数通すことで目的のコマンド列をcutコマンドを使って取り出す手法
これはkanata師のls --helpからdateを取り出すやつを元にやってみた(上記サイト参照)

それがスライド13ページにあるやつ

$ echo "うんこ"|base64|base64|base64|cut -c 4,27|awk '{print tolower($1)}'
sl

うんこがSLを走らせる
まさに 産業革命

ちなみに目grepがつらい人たちのために

$ echo "もじれつ"|fold -w1|cat -n|grep ほしいやつ|awk '{print $1}'

これで何文字目かわかるのでcutしまくろう

これからの展望

なんかうんこを使うのが通例でトレンドなようで、様々なコマンドがうんこから錬成されてるみたいですね・・・ここまでくるとうんこに無限の可能性があるように感じてきます
どうやらシーザー暗号とかopensslを利用する手もあるようなのでそっちも試してみたいですね

日本語base64難読化シェル芸の方はさらなる隠蔽を目指してコマンド全体を覆いつくせるように模索しているところです

何かわかったらシェル芸勉強会のLTで話してみたいですね

競プロとかのテストケースを自動化したい

蜘蛛ってこわい

家蜘蛛っていいやつなのは知ってるけどデカいと怖い

テストケースを自動化したい

いまさら競プロを始めた隊長です
いまはABCを中心にやっています

基本的に
問題読む

コード書く(長考)
↓  ↑
問題のページに書いてあるテストケースを試す

提出

って流れですけどこの テストケースを試すとこが正直ダルい
そう思いませんか!?

なのでVisualStudioの単体テスト機能を使ってこれを自動化したいんですけど、ggってもあんまり出てこないので書きました。

自動化する

これをテストしたぃょ・・・

public class Calc{
    public void Solve(){
        var item=int.Parse(Console.ReadLine());
        Console.WriteLine(item);
        return;
    }
}

適当なテキストファイルにテストケースの入力と出力を一行開けて並べるだけで全部チェックしてほしぃょ・・・

123

123

456

456

↑のテキストファイルをtestcase.txtという名前でプロジェクトに追加して、以下の単体テストを実行すると・・・????

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Text;
using System.IO;

namespace CS_AtCoder.Tests {
    [TestClass()]
    public class CalcTests {
        private static bool[] active = new bool[4] { false, false, false, false };
        private static StringBuilder[] expected = new StringBuilder[4];
        private static StringBuilder[] Stdin = new StringBuilder[4];
        private const string testcasePath = @"..\..\test_case.txt";
        Calc Target = new Calc();
        [ClassInitialize()]
        public static void ClassInit(TestContext tc) {
            int cnt = 0;
            string s;
            StreamReader sr = new StreamReader(testcasePath);
            while (sr.Peek() >= 0 && cnt < 4) {
                var sb = new StringBuilder();
                while ((s = sr.ReadLine()) != "" && sr.Peek() >= 0) {
                    sb.Append(s);
                }
                Stdin[cnt] = sb;
                StringBuilder expect = new StringBuilder();
                if (sr.Peek()<0) break;
                while (sr.Peek() >= 0 && (s = sr.ReadLine()) != "") {
                    expect.Append(s);
                }
                expected[cnt] = expect;
                active[cnt] = true;
                cnt++;
            }
            sr.Close();
        }

        private Tuple<StringWriter, StringReader, StringBuilder> MakeRedirect(int index) {
            var sb = new StringBuilder();
            var sr = new StringReader(Stdin[index].ToString());
            var sw = new StringWriter(sb);
            Console.SetIn(sr);
            Console.SetOut(sw);
            return new Tuple<StringWriter, StringReader, StringBuilder>(sw, sr, sb);
        }

        //↓と同じようなのを好きなだけ並べる
        [TestMethod]
        public void Case0() {
            int index = 0;
            if (!active[index]) { Assert.IsTrue(true);return; }
            var item = MakeRedirect(index);
            Target.Solve();
            item.Item1.Close();
            item.Item2.Close();
            var actual = item.Item3.ToString().Replace("\r\n", "");
            Assert.AreEqual(expected[index].ToString(), actual);
        }
    }
}

unittest1

やったあぁぁぁ

やってること

やってることはただ単にStringBuilderに入力と出力を格納し、入力の方をStringReaderとしてstdinに割り当てる。
もう一つStringBuilderを用意し、それをStringWriterとしてstdoutに割り当てる。これをほしい出力を格納したStringBuilderAssert.AreEqualしてあげるだけです。

ちなみにテストケースは5つ以上できません。
まあでもだいたい5つもないしな~みたいなw
余ったテストメソッドはIsTrueでOKだします

もう少しいい書き方もあるだろうけど、これで動くのでこれでいいです。 テストケースをコピペするのもなんか自動化できないかな~とか思いますけどまぁこれでいいです。

よーし!めっちゃはかどりそうです!