オルトプラスエンジニアの日常をお伝えします!

テーブル駆動テスト in C++

この記事はAltplus Advent Calendar 2017の13日目のエントリです。

最近なぜかJava関連のアカウントにTwitterでフォローされることが多い、JavaマスコットのDukeを愛してやまない竹田です。

テーブル駆動テスト

今回は、Go wikiで紹介されて、Goコミュニティでよく使われている(?)テーブル駆動開発をC++でやってみたいと思います。

テーブル駆動テストとは、テストの入力と期待値をテーブルの行に記載し、テーブルを走査しながら実行していくテストのことです。

例えば、int型の入力を受け取って、2倍する関数int twice(int x)のテスト・テーブルは以下のようになります。

in(入力) out(出力)
0 0
1 2
2 4

このテーブルを使って、テーブル駆動テストを書くと以下のようになります。

#include <cassert>

int twice(int x){
    return x * 2;
}

int main()
{
   struct { int in; int out; } table[]{
       {0, 0},
       {1, 2},
       {2, 4}
   };
    
   for(auto [in, out]: table){
    assert(twice(in) == out);
   }
}

テスト・ケース毎にAssertを書く必要がなくなり、テーブルに入出力がまとめられているため、関数の入出力仕様に集中することができます。入出力で仕様が決定される純粋な関数のテストに役立ちます。

関数の引数が増えてもテーブルの列を追加するだけで対応できます。文字列とインデックスを取って、インデックス番目の文字を返すat関数のテーブルは、

文字列(in1) インデックス(in2) 文字(out)
"abc" 0 'a'
"Hello" 3 'l'
"Hello world!" 7 'r'
#include <cassert>
#include <string_view>

char at(std::string_view s, int index){
    return s[index];
}

int main()
{
   struct { char const* str; int index; char out; } table[]{
       {"abc", 0, 'a'},
       {"Hello", 3, 'l'},
       {"Hello world!", 7, 'o' }
   };
    
   for(auto [s, i, out]: table){
    assert(at(s, i) == out);
   }
}

最後に

テーブル駆動テストを利用すると、関数の入出力仕様にフォーカスしたテスト・ケースを見通しよく記載することができます。

C++の"unnamed struct"(テーブルの定義に利用している名前なしのstruct)、"range-based for"、"structured bindings"を利用して、快適なテーブル駆動テストにしましょう!

自動でZIP!deポン!

この記事は Altplus Advent Calendar 2017 (https://qiita.com/advent-calendar/2017/altplus) の12日目のエントリです。

※Qiitaに書いた記事を転載しています。

こんにちは、自動化大好き オルトプラスラボの嶋田大輔(id:cimadai) です。

寒さも厳しくなってきた今日この頃ですがみなさまいかがお過ごしでしょうか。

さて今日はポンポンポポポン!という掛け声と共に始まるZIP!deポン!というゲームを自動化する話を書こうと思います。

ZIP!deポン!とは?

ZIP!deポン!というゲームは、日テレで放送中の朝の情報番組「ZIP!」の中の視聴者参加型コーナーのことです。

このコーナーは一日の放送中に「6:20頃」「6:50頃」「7:50頃」の3回実施されます。

各回でクイズが出題され、当たれば3ポイント、外れても1ポイントもらえます。

20ポイントが貯まるとGoogleHomeやオーブンレンジ、カニといった魅力的な商品のプレゼントに応募することができます。

また、各ポイントの集計期間は2週間毎になりますので、10営業日 x 3ゲーム = 30回のポイント取得チャンスがあります。

なぜ自動化するのか?

私の務めている会社は10時始業のため、それに合わせて朝も比較的ゆっくり起きることになります。 そうすると必然的に6時台はまだ夢の中にいるため、7:50頃のゲームにしか参加できず、なかなかポイントが貯まらないため応募するのが難しい状態となります。

具体的には毎日7:50だけ参加したとしてもギリギリ20ポイント行くかいかないか、という感じです。

6時台に行われている2回のゲームを実行することができれば全て外れたとしても確実に30ポイントとれますので応募出来ます。

どうやるのか?

株式会社グラモ から販売されている iRemocon という製品を利用します。 iRemoconは簡単に言えばスマホから操作可能な高機能学習リモコンです。 家の中(同一ネットワーク)はもちろん、外出先からも操作できるので帰宅前にエアコンを先につけておく、というような事ができます。

また、毎日決まった時間にボタンを実行するなどのスケジュール機能や、一つのボタンに複数のボタン押下シーケンスを組み込むマクロ機能もあるので、いろいろな操作を一アクションにまとめることができます。

今回はこのスケジュール機能を使ってZIP!deポン!を自動化してみます。

やりたいこと

まずやりたいことを決めましょう。

やりたいことは、以下の一連の流れです。 1. 私が寝ている時間帯のゲームの時にTVをつけ 2. 青赤緑黄のカラーボタンのいずれかを押して 3. 当たりか外れかを見定めて 4. TVの電源を切る

3で当たりか外れかを見定める理由としては、データ放送の注意点として以下の注意事項があるためです。

「ZIP!deポン!」の各コーナー中、リモコンの色ボタンで選択後にテレビの電源OFFやチャンネル選局などで、ゲーム結果をご確認していない場合、ポイントが加算されません。ご注意ください。

やってみる

気をつけたいのが、ZIP!deポン!は「6:20頃」「6:50頃」「7:50頃」とありますが、 「頃」とある通り、きっちりその時間に始まるわけではなく、数分のズレがあります。

なのでそのズレを吸収しなければなりません。

ここでは手っ取り早く前3分、後7分くらいの範囲でTVを付け、その間一定間隔でカラーボタンをいい感じの時間をあけつつ押して見ることにします。

※本当であればマクロ機能を使ってワンボタンでスケジュール実行したかったのですが、 残念ながらスケジュール実行はマクロに対応していないので個別でスケジュールを組んでいます。

やりたいことを実現するために、以下の4つのボタンを作りましょう。 ※赤である必然性は特にはないのですが、どのボタンを押してもきちんと動作していれば30ポイント取得できるはずなのでどれでも大丈夫です。

  • 「TVの電源ON」ボタン
  • 「4チャンネル切替え」ボタン
  • 「カラーボタン赤」ボタン
  • 「TVの電源OFF」ボタン

ボタンは iRemoconの UIデザイナー で作成していきます。

次に、端末上でiRemoconアプリから「リモコンタイマー設定」で各ボタンのスケジュールを設定します。

12/4時点の初期設定

各回の開始3分前からTVを付け、TV電源ONから1分後に4chに切り替え。 並行して、毎分赤ボタンを送出するようにスケジュール。 恐らくゲームが終ったであろう7分後にTV電源OFFする。

ボタン スケジュール
赤ボタン 毎分
TVの電源ON 毎日 6:17, 1日毎 6:47
4ch切り替え 毎日 6:18, 1日毎 6:48
TVの電源OFF 毎日 6:27, 1日毎 6:57

12/5に修正した設定

12/5の結果を受け、もしかしたら電源OFFが早すぎるのかも?という仮説のもと、電源OFFをそれぞれ3分遅くした。 他の設定はそのまま。

ボタン スケジュール
赤ボタン 毎分
TVの電源ON 毎日 6:17, 1日毎 6:47
4ch切り替え 毎日 6:18, 1日毎 6:48
TVの電源OFF 毎日 6:30, 1日毎 7:00

どうなったか?

12/4から上記設定で運用を始め、一週間の成果は以下の通りです。

日付 成果 獲得ポイント 累計ポイント
12/4 (月) 自動タイマー処理が上手くできてなかったため6:20と6:50は取り逃す。
1分毎に赤ボタンを押すように変更。
7:50のZIP!deポン!は1ポイント取得。
1P 1P
12/5 (火) 6:20と6:50のどちらかは成功(ハズレ)してどちらかは失敗、1ポイント取得。
7:50は正解、3ポイントゲット。
失敗原因を「電源OFFが早すぎた可能性がある」と仮定。
→電源OFFを各回開始後7分後OFFを10分後OFFに延長。
4P 5P
12/6 (水) 6:20と6:50のどちらかは成功(当たり)してどちらかは失敗、3ポイント取得。
7:50はハズレ、1ポイントゲット。
また失敗してしまったが、この日は特に設定を変更せずに様子見。
4P 9P
12/7 (木) 6:20と6:50のどちらかは成功(当たり)してどちらかは失敗、3ポイント取得。
7:50はハズレ、1ポイントゲット。
どっちが失敗しているのか謎。切り分けのため6:20を一時無効にしてみる。
4P 13P
12/8 (金) 6:50は成功(ハズレ)、1ポイント取得。
7:50は当たり、3ポイントゲット。
6:20が失敗している模様。6:20は2択なので押すボタンの問題か?
4P 17P

余談: 7:50の回のZIP!deポン!の開始/終了時刻

開始/終了時刻は結構ばらつきがある感じです。

日付 開始時刻 終了時刻
12/4 (月) 7:55 7:57
12/5 (火) 7:56 7:58
12/6 (水) 7:53 7:55
12/7 (木) 7:54 7:56
12/8 (金) 7:53 7:55

今後の展望

  • 現状はカラーボタンを毎分押しているのでひょんな時に誤動作しそうなので必要なタイミングでのみ送出するようにしたい。
  • ゲストが勝つ可能性が高いのでメインメンバー以外の人がでていたらそのボタンを押す、などの顔認識ができたらいいな。
  • Google Homeに聞いたら答えを教えてくれるようなことができると楽だな。

まとめ

一切頑張らずに1週間で17ポイントも集まったので2週間あれば20ポイントは余裕でいけますね。 みなさんも頑張らない懸賞生活をエンジョイしましょう!

ではでは

ストレッチをエンジニアリングしよう 〜その硬さ、ボトルネック特定できてる?〜

※この記事は AltPlus Advent Calendar 2017 の11日目のエントリです。

DSC_0290.JPG

みなさま年末進行お疲れ様です。

この時期、われわれエンジニアはいつもに増して座りっぱなし、コーディングしっぱなしですよね。んで、「体動かさなきゃなー」とか「ストレッチやったほうがいいんだろうなー」とか思いつつも腰が重くてなかなか行動に移せない諸兄がたくさん居らっしゃると存じます。

というわけで今回は体が硬いお二人に来ていただきました。


IMG_2011.JPG

カサトーさん ブロンドヘアが美しいソリッドボディ(割りと硬い)


ミオさん おしゃれパーマがよく似合う超合金ボディ(かなり硬い)


ご注意 効果には個人差があります。ストレッチは無理やり行うと思わぬ怪我の原因となります。無理せず痛くない範囲で行いましょう。


ストレッチをエンジニアリングする


「というわけでストレッチやるんですが、ストレッチ得意ですか?」


「そんなエンジニアあったことねーよ」


「やったほうがいいのはわかってるけど痛いし、効果感じにくくて続かないですね」


「うん、非常によくわかります。でもね、ちょっと視点を変えてみましょう。体が硬い、ストレッチしても効果出にくい、こういったことには原因があるはずです」


「うーん、そうはいっても全身硬いからね。身体ごと交換しないとダメなんじゃね?ってレベルで...」


「じゃあ、アプリに置き換えて考えましょう。もしアプリの動作がモッサリしてるときはどうですか?」


「あーそういう場合は何かしらボトルネックがあったりするよね。デバッグログが垂れ流しだったり、パーティクル画像がやたらとでかいピクセル数だったり」


「ですよね。でもボトルネックが特定できれば解決策を考えて実施できませんか?


「言いたいことは分かるんですが、問題認識や想定がずれてて効果が出ない場合ってありません?サービス運営で施策がコケる場合ってだいたいが...(ry


「もちろんです。そういう場合は認識済みの問題を更に分解して適切な粒度で抽出したり、ベンチマークの測定箇所を変更してボトルネックを見直すなど、分析をやり直してより効果的なソリューションを構築しますよね」


「なるほど、仮説を立てて検証するんだね。バグフィックスとかチューニングとよく似ているね!それならエンジニアにはぴったりかも!」


「まさにその通り!一度の施策で効果が出るとは限らないので、継続的なPDCAが大切です」


「台本通りかよ」

まずは現状把握をしよう


「ではお二人の体の硬さを見ていきましょう。まずはミオさんから」


「めっちゃ痛いです」


「ちょっとメジャーしかなくて雑な計測ですが立位体前屈-21cmですね。理想的な数値です。続いてカサトーさんを見ていきましょう」



(あれ、思ったより柔らかいな)


「チートじゃね?」


「柔らかいのになぜかdisられている」


「またしても雑な計測ですが開脚131cmです。マニキュアがきれいですね」


「褒めるとこそこかよ」


ボトルネックを突き止めろ!


「では、先程の体勢で特に痛かった、強く張りを感じた部分はどこでしょうか?」


「いや、もう全部痛くてとにかく辛い


「じゃあ質問を変えましょう、背中、腰、お尻、裏もも、膝裏を比較して最も張りを感じるのはどこでしょう?」


「いや、もう全部痛いんだって」


「なるほど、じゃあ5箇所すべてがボトルネックだと仮定しましょう。ですが同時に5箇所のボトルネックを解消するのって可能だと思いますか?」


「うーん、ひとつのcommitに5箇所も大規模な修正加えられたらキレるかな


「ですよね。なので一旦は裏ももにフォーカスして考えていきましょう。先程の体勢だと5箇所全てに負荷がかかるので、裏もも以外の負荷を抑えた体勢でストレッチしていきます」


「裏ももだけじゃなくお尻も伸びてる気がするけど、さっきみたいに痛くないから楽だね」


「膝を曲げた状態で前屈するとお尻〜裏ももが伸びてきますよね。痛くないので呼吸も乱れず安定したストレッチ動作になっています」


「私は内ももが痛かったんですが...」


「はい、では内ももをボトルネックとして仮定して進めていきましょう。ちなみに左右で痛みや張りの違いはありましたか?」


「うーん、どちらも同じくらいです。あ、でも右側のほうが開きが悪かった気がする」


「なるほど、では片側ずつ見ていきましょう」


「おお、右が硬いっすね!」


「左はあんなに柔らかったのにね!」


「硬いほうがウケる...そんなバカな」


「しかも右はお尻が浮いてしまってます。内ももの左右差より、お尻の柔軟性に左右差が大きいのかもしれません。内ももをボトルネックと仮定しましたが、案外お尻側に大きなボトルネックがあるかもしれませんね。というわけでお尻のストレッチもやってみましょう」


「あーこれすごいお尻が伸びる気がする」


「ボトルネックだと思って弄ってたら、実は隣にもっとでかいボトルネックが居たってやつかな。開発あるあるだね!」


「ほんとそれ。ちなみにこの体勢ができない場合はもう少し負荷を下げた体勢でやってみましょう」


「負荷下げてもツレー!!」


「特に痛い部...」


「全部ーー!!」

効果測定


「というわけで、ボトルネック分析とソリューションの構築、実施を行いました」


「そう言えばそういう趣旨だったよね」


「じゃあ次は効果測定ね」



「お、結構いいんじゃない?」


「立位体前屈 -15cmです。+6cm伸びてますね!素晴らしい!」



「あ、これは来たかも」


「開脚 149cmです。+18cm伸びてますね! これは予想を超える成果ですね!」


「やっぱチートなんじゃね?」


「」


「単純に開脚の数値が伸びただけじゃなく骨盤が立つようになって姿勢も良くなってますね。お尻のストレッチが効果的だったようです」


「そうですね、開脚だけでなく股関節の内外旋の可動域も広がった気がします」


「いきなりどうした...」

まとめ

対象者 体勢 ボトルネック 結果
立位体前屈 裏もも
(若干お尻)
+6cm(-21cm -> -15cm)
開脚 内もも
お尻
+18cm(131cm -> 149cm)

というわけで上記の結果を得ることができました。厳密な測定や評価ではありませんが「ストレッチもボトルネックを突き止めて解決して行くという点ではエンジニアリングとよく似ているね」という雰囲気が伝われば幸いです。

文中にもありましたが、一度の分析や実施では効果が得られない場合もあるのでトライアンドエラーを繰り返しつつより効果的なソリューションを構築してゆくのが大切です。また、一定の成果を得られた後も現状に満足せずさらなる高みを目指してPDCAを回してくださいね!



「ちなみにミオさんは5箇所中、1箇所の対応で+6cmの効果が得られましたよね。ってことは5箇所全部に対応を入れたら...?」


「+30cm!!」


「・・・雑な計算だな」

それでは良きストレッチライフを!


ご注意 効果には個人差があります。ストレッチは無理やり行うと思わぬ怪我の原因となります。無理せず痛くない範囲で行いましょう。