ラベル iutest の投稿を表示しています。 すべての投稿を表示
ラベル iutest の投稿を表示しています。 すべての投稿を表示

2020年12月1日火曜日

[C++] Concepts で外部ライブラリの template よりも優先されるオーバーロード関数を書く

※この記事は C++ Advent Calendar 2020 一日目の記事です。

はじめに

 C++20 で Concepts が導入されました。
コンセプト - cpprefjp C++日本語リファレンス

コンセプトはテンプレートパラメータを制約する機能で、「コンセプトによって関数オーバーロードやテンプレート特殊化」ができます。
今までテンプレート関数のオーバーロードを書く場合は SFINAE などのテクニックを使って書いていたと思いますが、コンセプトで書きやすくなりました。
これも大きな変化の1つですが、コンセプト関数が通常の関数よりも優先して lookup されるのも便利になった点の1つです。
コンセプトによるオーバーロードの場合、コンセプト関数が通常の関数よりも優先して lookup され、要件を満たすコンセプト関数がなかったら他の候補を探しに行きます。SFINAE では優先順位がつけられないケースでもコンセプトなら可能になりました。

どういうこと?
SFINAE では解決できなかった優先順位問題とコンセプトを使った実装の例

↑こちらは、std::cout << できない型の場合は "unknown" を出力するようにしようとしている様子です。

donot-change.h には std::cout するだけの template 関数 print があります。
こちらは変更ができない想定です。(外部ライブラリとか)

SFINAE.cpp では頑張って、 std::cout ができない場合で特殊化しオーバーロードしようとしてます。他の書き方を書いたりもしましたが、どうしても ambiguous になってしまって条件を満たすことができませんでした。

一方 Concepts.cpp は printable ではないコンセプト制約でオーバーロードするだけで実現できています。
すごく簡単ですね。

ちなみに
ちなみに donot-change.h を変更可能とした場合は、以下のように書くことができます。
namespace printer_internal
{
template<typename Elem, typename Traits, typename T>
::std::basic_ostream<Elem, Traits>& operator << (::std::basic_ostream<Elem, Traits>& os, const T&)
{
    return os << "unknown";
}
}

// 解決順序
// foo::operator <<
// ::operator <<
// ::printer_internal::operator <<
template<typename T>
void print(const T& val)
{
    using namespace ::printer_internal;
    ::std::cout << val << ::std::endl;
}
これは ADL の解決順序を利用して実現しています。
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ https://wandbox.org/permlink/rlQoH37cvYTI23qo 
使い所は?
例でも示したとおり、オーバーロードしたい対象の関数を変更できるのであれば、そちらで解決する方法を模索するのが良いと思います。
ただ、外部ライブラリなど変更することができない場合、非侵入的に解決する方法として覚えておくと役に立つ日が来るかもしれません。

実例
そもそも私がなんで外部ライブラリの挙動をオーバーロードで変更したいと思ったのかというと、以下の理由でした。

* Google Test はアサーションが失敗すると、そのときの値を出力します
* std::cout できない場合はバイト列を出力します
* できない場合の対応は「ちなみに」の ADL を利用した方法で実現されています
* 古い Google Test ではこの機能がない
* 自作テスティングフレームワーク iutest は Google Test と互換性がある
* iutest は Google Test の拡張機能としての一面もある
* 古い Google Test と iutest の組み合わせでも不明な型を printable にしたい
* Google Test 側のコードは書き換えられない
* どうしよう

実際にコンセプトで対応してる部分はこちらになります。
最後に
教えていただいた いなむのみたま(@mitama_rs) 先生ありがとうございました。
ちなみにそのツイートにもぶら下がってますが、C++20 未満のコンパイラでこの問題を解決する方法も募集しておりますmm


2020年8月12日水曜日

[iutest] ドキュメントの置き場を GitHub Pages に集約した

issue いただきまして iutest のドキュメントの構成を変更しました。
(issue ありがとうございますmm)

これが、

こうなりました。

ドキュメントを GitHub Pages へ
まず OSDN においていた最新バージョンのドキュメントを GitHub Pages に引っ越ししました。
こちらのドキュメントは実は2年くらい更新をし忘れていました。。。
そこでドキュメントの更新は GitHub Actions でタグが作られたら自動で更新されるようにしました。

次にもともと GitHub Pages においていた master ブランチ HEAD バージョンのドキュメントですが、こちらはトップディレクトリから master ディレクトリに移動しました。ディレクトリ構成は以下のとおりです。

最新バージョンのドキュメントは latest ディレクトリに push されます。
OSDN からリダイレクト
続いてもともと OSDN にアップロードしていたドキュメントページにアクセスしたら、GitHub Pages にリダイレクトするように設定します。

まずはサーバーへのアクセスをします。(これまでもやってるはずですが、いつも忘れてしまうのでメモ)
OSDN のドキュメントはシェルサーバーの「/home/groups/<プロジェクトUNIX名の1文字目>/<プロジェクトUNIX名の1、2文字目>/<プロジェクトUNIX名>/」にあります。iutest の場合は「/home/groups/i/iu/iutest」です。サーバーには鍵を登録してアクセスします。

OSDN では .htaccess でリダイレクトの設定ができるので、OSDN ドキュメントのサンプルを参考に設定しました。
RewriteEngine on
RewriteBase /doc
RewriteRule ^(.*) https://srz-zumix.github.io/iutest/latest/$1 [R=301,L]

最後に
手動更新は早急にやめたほうがいいですね。
では。





2020年7月28日火曜日

iutest v1.17.1 をリリースしました

C++ テスティングフレームワーク iutest v1.17.1 をリリースしました。
Github: https://github.com/srz-zumix/iutest/releases
OSDN: https://osdn.net/projects/iutest/releases/p12829

変更点は以下の通りです。

* New
* GTEST_SKIP 対応
* IUTEST_*_NE_RANGE アサーションを追加
* IUTEST_*_NE_COLLECTIONS アサーションを追加

* Improved
* ::std::wstring_view / ::std::u16string_view / ::std::u32string_view 対応
* Variadic Templates 非対応コンパイラーでの型パラメータの型表示を改善

* Changes
* アサーションに operator << したときの出力を PrintToString したものと同じになるように変更
* iuwandbox: Wandbox 向けにサイズ圧縮したヘッダーをデフォルトで使用するように変更
* iuwandbox: 上記に伴いフルバージョンのヘッダーを使うオプション(--no-iutest-use-wandbox-min)を追加
* deprecated: iuwandbox: --iutest-use-wandbox-min
* Python2 のサポート終了

* Bug fixes
* C++20 で削除された basic_ostream::operator << に対応
* IUTEST_AP が同じ名前空間に複数定義できない問題を修正
* Google Test 旧バージョンとの互換性を修正
* その他いくつかの不具合を修正



では。

2020年3月27日金曜日

[CircleCI] iutest のパイプラインを更新した&お詫びと訂正

さて今週も iutest の CI パイプライン更新を紹介します。
先週はこちら「ブログズミ: [Azure Pipelines] iutest のパイプラインを Multi-stage pipelines に変更した
今週は CircleCI です。

YAML の整理
まずは CircleCI のサンプルを参考にして、YAML をキレイに変更しました。
もともと gcc/clang でそれぞれ 4 パターンビルドしていたジョブを、アンカー・エイリアスを駆使して gcc/clang 別々のジョブに分けました。



以下、YAML の Before/After になります。
Before


After


ファイルの先頭部分はあまり変わってないですが、ファイルの末尾あたりにあるジョブの定義とワークフローの定義が YAML のアンカー・エイリアスを使ってとてもシンブルになっていると思います。

CI が定めた名前のキーではなく、任意のキーでアンカーを作って再利用するやり方はとてもいいなーと思いました。
(CI サービスによってはスキーマにないキーを許容しないサービスもあった気もするが・・)

GitHub PR の Checks に表示する
PR の Checks に表示するには「CircleCI Checks」をインストールする必要があったみたいだったので、遅ればせながらインストールしました。

「Organization Settings」の「VCS」に「Manage GitHub Checks」ボタンがあるのでそちらでインストールできます。
権限確認と適用する範囲を決めたら完了です。



このように表示されます。



お詫びと訂正


本書で、Circle CI の 4 並列制限がなくなったと書いていましたが、あれは真っ赤なウソでした。
お詫びして訂正致します。

今回並列数を増やそうとして気づきました。。




実際には、コンテナの並列数は今までどおり 4 つまでとなっているようです。(ただ、これは CircleCI 1.0 のころの設定画面なので CircleCI 2.0 になってどう管理されいるのか、まだ良くわかってません)
また、OSS プランはベースがあくまでも Free プランなので、ジョブの並列数は Pricing 記載の通り 1 つまでの制限になっているようです。
ジョブが1つずつ実行されていることを確認しました。



これを機に、各 CI サービスの並列実行のサンプルリポジトリを作成することとしました。
近いうちに CI Specs に付け加える予定です。

以上。

2020年3月16日月曜日

[Azure Pipelines] iutest のパイプラインを Multi-stage pipelines に変更した

技術書典8 (技術書典 応援祭)で本を出しましたが、本を書いてていくつかのサービスで新しい機能に追従できてないことが発覚したため、ちょこちょこと更新をしていっています。
今回は、iutest の「Azure Pipeline」を「Multi-stage pipelines」に移行しました。

移行結果
まずはじめに移行してどうなったのか見ていただこうと思います。
Before

After


ながいので一部分だけのキャプチャですが、なんとなーく見やすくなったような気がしますね。
テストの種類ごとにステージを分けて、ステージの依存と並列化をしました。

Preview features の有効化
Multi-stage pipelines は現在 Preview feature のため、設定で有効になっている必要があります。
アカウントのボタンを開くと「Preview features」メニューがありそこから変更ができます。


(組織の管理者権限があれば、個人か組織どちらの設定をするか選択できます。)
ここで「Multi-stage pipelines」が「On」になっているか確認してください。
(自分の場合有効化した記憶がないのですが、もしかして Multi-stage な YAML を push すると自動で有効になる??この機能が発表されたのが去年の 5 月なので、以前に有効化していた可能性もなにきしもあらず)




dependsOn 設定で依存と並列
ステージは dependsOn で依存関係を定義できます。iutest では Test/ConfigDisabledTest/ConfigNoFeatureTest が Precheck ステージに依存してます。
また、依存先が同じステージは並列に実行されます。つまり、下記 YAML では Test/ConfigDisabledTest/ConfigNoFeatureTest が並列になります。
stages:
  - stage: Precheck
    jobs:
      - job: syntax_test
        pool:
          vmImage: ubuntu-16.04
        steps:
          - script: make -C test -j4 syntax_tests_run
            displayName: 'syntax test'
  - stage: Test
    dependsOn: Precheck
    jobs:
      - template: .ci/azure_pipelines/template-make-test.yml
        parameters:
          name: default
  - stage: ConfigDisabledTest
    dependsOn: Precheck
    jobs:
      - template: .ci/azure_pipelines/template-make-disabled-test.yml
        parameters:
          name: disabled
  - stage: ConfigNoFeatureTest
    dependsOn: Precheck
    jobs:
      - template: .ci/azure_pipelines/template-make-nofeature-test.yml
        parameters:
          name: nofeature

依存するステージがない複数のステージを並列にしたい場合は以下のように記述します。
dependsOn がないと Test1 → Test2 の順で逐次実行されますが、「dependsOn: []」とすることで Test1/Test2 が並列実行されます。
dependsOn がないと1つ前に書かれているステージに暗黙的に依存すると考えてるとよさそうです。
stages:
- stage: Test1

- stage: Test2
  dependsOn: []

最後に
Multi-stage pipelines にしたことでステージ単位でグルーピングでき、見た目がわかりやすくなったと思います。また、依存関係もステージ単位でできようになり楽になりました。

今回は以上です。では。



宣伝

2019年12月6日金曜日

[C++] 本当に private なところ

※ この記事は「C++ Advent Calendar 2019」6日目の投稿です。


private メンバー変数の R/W テスト
まずはこちらをご覧ください。
#include "iutest.hpp"
//origin>> #include "../../include/iutest.hpp"
#include <iostream>

int main(int argc, char** argv)
{
    IUTEST_INIT(&argc, argv);
    return IUTEST_RUN_ALL_TESTS();
}

class Test
{
private:
    int A:12;
    int B:4;
    int C:4;
    int D:12;
    int E;
public:
    Test()
        : A(0x777)
        , B(0x3)
        , C(0x4)
        , D(0x400)
        , E(1)
    {}
};

IUTEST_MAKE_PEEP(int Test::*, Test, E);

IUTEST(PeepTest, MemberVariable)
{
    ::Test test;
    IUTEST_EXPECT_EQ(1, IUTEST_PEEP_GET(test, ::Test, E));
    IUTEST_PEEP_GET(test, ::Test, E) = 4;
    IUTEST_EXPECT_EQ(4, IUTEST_PEEP_GET(test, ::Test, E));
}
https://wandbox.org/permlink/GqDQ4zeCrzv1HABq

private メンバーである Test::E への書き込み・読み込みができていることがわかると思います。

private メンバー変数(ビットフィールド)の R/W テスト
続いて、上でアクセスしている private 変数を E から A に変えてみます。

IUTEST_MAKE_PEEP(int Test::*, Test, A);

IUTEST(PeepTest, MemberVariable)
{
    ::Test test;
    IUTEST_EXPECT_EQ(1, IUTEST_PEEP_GET(test, ::Test, A));
    IUTEST_PEEP_GET(test, ::Test, A) = 4;
    IUTEST_EXPECT_EQ(4, IUTEST_PEEP_GET(test, ::Test, A));
}
https://wandbox.org/permlink/NRPvHweGaDp6HAdy

今度はコンパイルエラーです。

解説
プライベートにアクセスした方法
上のコードで利用したテスティングフレームワークは iutest です。
private メンバーへのアクセスはこのフレームワークの PEEP 機能を利用しています。
PEEP は template explicit instantiation で private にアクセス可能なことを利用して、private メンバーのアドレスを取得して実現しております。
詳しくは昔書いたこちらの記事を参考にしてください。(「ブログズミ: [C++] Private な関数のテスト」)

ビットフィールドにアクセスできなかった理由
error: address of bit-field requested
上記 PEEP の方法ではアドレスを取得できる必要があるため、
アドレスが取れないビットフィールドではアクセスできないわけです。
(言語法律家によるアクセスが可能な場合はビットフィールドでもアクセスできますが・・ : https://wandbox.org/permlink/zYC1hAYuqqixaEUn

まとめ
C++ にプライベートはあった

2019年11月13日水曜日

iutest v1.17.0 をリリースしました

C++ テスティングフレームワーク iutest v1.17.0 をリリースしました。
Github: https://github.com/srz-zumix/iutest/releases
OSDN: https://osdn.net/projects/iutest/releases/71920

変更点は以下の通りです。

* New
* ::std::string_view 対応
* ::std::filesystem 対応
* FreeBSD 対応
* ALMOST_EQ アサーション を追加
* --iutest_locale_ctype オプションを追加

* Improved
* setlocale(LC_CTYPE, IUTEST_FLAG(locale_ctype)) をテスト実行開始時に行うように修正
* Visual Studio 2019 対応

* Changes
* 大きな配列/コンテナ/オブジェクトの pinter を修正
* iutest_main で setlocale していたものを廃止
* junit xml 出力で出力するテストが 0 だった場合にファイル書き出ししないように修正
* CSV パラメータ生成でファイルオープン失敗した場合の警告レベルを FATAL から WARNING に変更
* CSV パラメータ生成で要素がなかった場合に WARNING 出力
* deprecated: IUTEST_USE_OWN_LIST

* Bug fixes
* いくつかの不具合を修正


一応、v1.17 になって C++17 に対応した。という体でリリースしてます。
ホントはいろいろ足らない気がしてますが、ひとまず filesystem と string_view の対応はできると思います。
実際に使用してみたら、全然使い物にならない・・ってこともあるかもしれないので、issue 報告お待ちしております。

では。

2019年1月22日火曜日

[GitLab CI] マトリックスを組んでみた

GitLab CI でマトリックスを組んでみました。

Matrix Builds in CI - Questions & Answers / GitLab CI - GitLab Forum
方法は2つあって、YAML のアンカーを使った方法と、それと同等の extends を使った方法です。
(マトリックス分べた書きすればどんな CI サービスでも複数ジョブは実現できるが、重複する冗長な部分をまとめて簡潔に書けますよーという感じ)

Anchor を使った方法
YAML にはアンカーとエイリアス記法が存在します。
これは GitLab の YAML の機能ではなく、YAML の標準的な記法のため、別の CI サービスでも応用できると思います

Anchors - Configuration of your jobs with .gitlab-ci.yml | GitLab
以下は公式の例を参照しています。
.job_template: &job_definition  # Hidden key that defines an anchor named 'job_definition'
  image: ruby:2.1
  services:
    - postgres
    - redis

test1:
  <<: *job_definition           # Merge the contents of the 'job_definition' alias
  script:
    - test1 project

test2:
  << *job_definition           # Merge the contents of the 'job_definition' alias
  script:
    - test2 project

&名前 でアンカーを作成し、*名前でアンカーを参照します。
また、 「<< *名前」とすると、アンカーの内容をマージしてくれます。

つまり、共有部分をアンカーとして定義し、マトリックスジョブに可変部分を定義、あとは共有部分をマージすれば簡単にマトリックスを組むことができます。
(今回は後述の extends で書いたので実例は省略)

extends を使った方法
こちらは GitLab 11.3 で導入された GitLab の機能になります。
アンカーを書かずとも、extends で指定した YAML 要素を展開できる機能のようです。
アンカー記法やその参照方法を知らなくても、より直感的に書けるのでこちらをオススメします

extends - Configuration of your jobs with .gitlab-ci.yml | GitLab
アンカーの例を extends で書き直すと以下のようになります。」
.job_template:
  image: ruby:2.1
  services:
    - postgres
    - redis

test1:
  extends: .job_template
  script:
    - test1 project

test2:
  extends: .job_template
  script:
    - test2 project
(アンカー記法を見たあとだと、どっちでも良くない?となりますが、知らない人からしたら読みやすくなってるとは思います。)

iutest-test での実例
さて、最後に実例として iutest-test の例を紹介したいと思います。
iutest-test がどういったもので、どんな CI をしているかは以前に書いたこちらの記事を参考にしてください。
ブログズミ: iutest のテストをするリポジトリ iutest-test を GitLab/GitLab CI に引っ越しました

今回、マトリックスを組むことにした理由は、CI するブランチの対象を master のみから、master|develop に変更しようと思ったからです。
iutest-test でしているテストの重要度は高いわけではないので、1日1回 master ブランチを対象としていましたが、やっぱり master にマージしてからじゃないと状態がわからないのはよくないなと思い、develop でも1日1回実行すようにしました。


YAML はこちら→iutest-test / .gitlab-ci.yml


これで、定期ビルドでサブモジュールの更新が master / develop 2つに対して行われるようになりました。

最後に
今回のような定期ビルドの環境変数をマトリックスにしたいようなケースはパイプラインでなくても、トリガー固有の環境変数定義でもできると思います。
ただ、YAML のアンカーにしろ、extends にしろ、書き方を知っておくと、いざというときに役に立つのではないかと思います。

というわけで、早速アンカーを使った他の CI サービスの YAML を DRY にしようと思います。
今回は以上です。
では。

2018年11月20日火曜日

iutest のテストをするリポジトリ iutest-test を GitLab/GitLab CI に引っ越しました

iutest の外部テストリポジトリとして iutest-test を作成し、そこで Google Test との互換性テストを行ってきましたが、別リポジトリにしたのなら、GitLab に引っ越しして GitLab CI を使えるのでは?
ということで引っ越ししました。

Sign Up & Import
まずは GitLab への SignUp をしましょう。(GitLab は有名なので省略)
GitLab は GitHub からリポジトリをインポートできるので、その機能を使って iutest-test を持ってきます。


New Project から Import project を開いて、GitHub を選択(いっぱい対応してますね)


インポートしたいリポジトリを選択して、Import ボタンを押したら完了です。






簡単ですね!

.gitlab-ci.yml の作成
続いて、CI の設定をします。
もともと iutest-test では Travis-CI で定期ビルドして、iutest のサブモジュール更新を行い、
その push トリガーによって、Codefresh でテストを実行していました。
(そのときの記事はこちら→「」)

今回 GitLab に引っ越してすごく良かったのが、この2つのビルドを GitLab CI 1つで完結できたことです。できあがった .gitlab-ci.yml は以下になります。


そして、Google Test 最新コミットとの互換性テストの構成はこのようになりました。


画像の上半分あたりは、
ブログズミ: 依存ライブラリの GitHub を Travis CI で定期的に監視して DockerHub Automate Build を実行する(+ Codefresh ジョブを Trigger)」で紹介した内容とほぼ同じです。
(違いは Webhook 先が変わったのと、Google Test の更新検知をサブモジュールにしたくらい)

サブモジュールの更新
update_submodule のパイプラインがサブモジュールの iutest の更新を担います。
こちらは、毎日1回のスケジュールトリガーで起動します。
特に難しいことはなく、サブモジュールを最新にしているだけです。
リモートに push する部分ですが、Protected な環境変数に記録した秘密鍵を使って行います。
やり方はこちらを参考にしました。
.gitlab.ci.yml for SSH with private key.

Deploy Key の登録
まずは公開鍵を登録します。
「Settings」 > 「Repository」の「Deploy Keys」から登録してください。


push のためのキーを環境変数に設定する
https://docs.gitlab.com/ee/ci/variables/#variables
「Settings」 > 「CI/CD」 に 「Variables」 があるのでそこで設定します。
Protected にしてくださいね!


> They can be protected by only exposing them to protected branches or tags. 
ちなみに、ここで設定した Protected Variable は Protected branch or tag でしか使えないので注意!
ブランチ設定は「Settings」 > 「Repository」 > 「Protected Branches」でできます。


update_submodule ブランチの意味は?
サブモジュールを更新して master ブランチに push する update_submodule パイプラインですが、これを行うのは master ブランチではなく update_submodule ブランチです。
なぜ、別ブランチで行っているかというと、バッジのためです。

サブモジュールを更新するパイプラインも立派なパイプラインの1つです。
バッジはブランチ単位で発行できますが、パイプラインでわけることができないので、同じブランチでやってしまうと「テストパイプラインは失敗している」「サブモジュール更新は成功している」ときに、正しく最新のテスト状況を表示できません。
そのため、ブランチを分けています。
(パイプラインごとにバッジ出せると嬉しいですが・・・)

テストの実行
テストの部分は前回から特に変わってません。
Google Test の最新コミットが入った docker コンテナを落としてきて、その中で iutest の互換性テストを実行しています。
前回との差分としては、DockerHub からの Webhook が latest と latest-alpine の2つが飛んでくる(release があるとそのタグも)ので、 latest のみパイプラインを起動するようにフィルタリングするようにしました。
フィルタリング部分は最近お気に入りの Integromat で行いました。
そのへんは別の記事を参考にしていただければと思います。
ブログズミ: Travis CI の結果をツイートするのを Zapier から Integromat に引っ越した

これで完成、あとは・・・
バッジを付けましょう!お約束ですね。
GitLab CI のバッジは、「Settings」 > 「CI / CD」 > 「General pipelines」 を展開すると、「」があるのでそちらで取得できます。



(失敗してるのは気にしないでください・・・)
pipeline status
ところで、そもそもなんでリポジトリを分けてるの?
(このへんもブログにしとけばよかったなーと思いつつ)・・・理由は簡単で関連先が理由でテストが失敗したままになるのが困るからです。
iutest は独立したフレームワークですが、Google Test の Add on としての機能を持っていたりするので、Google Test を使ったテストもしています。
PR とかあったときに、Fail となっても困惑を生むだけだと思ったので、テスト用にリポジトリを分け、さらに粒度も1日1回程度にしました。

最後に
個人的に、外部依存のライブラリとのテスト構築のパターンが出来上がった、という感想。
こう、カチッとハマった感じはいいですね。

今回は以上です。
では。



2018年11月8日木曜日

travis-ci.org から travis-ci.com に移行しました



Travis CI といえば travis-ci.org だったんですが、
GitHub Checks API を試してみようと思って調べていたら
(そのへんのことはこちら→「ブログズミ: GitHub Checks 対応 CI サービスを使ってみた」)
travis-ci.com のことを知りました。

.com の方は今までは public リポジトリ(OSS)が対応していなかったようですが、public リポジトリも .com を利用可能になりました。
そして、GitHub Checks API に対応しているのは .com の方なので、移行が必要でした。

すでに .org の方でビルドされているプロジェクトは、すぐには .com は使えなくマイグレーションが必要です。(新規の場合は .com で登録できる)
ただ、マイグレーションに関しては順次行われるようですが、遅延しているらしく、すぐに .com に移行したい場合はサポートにメールで依頼する必要があります。
iutest で Checks API を使いたかったので、メール依頼を出してみました。





マイグレーションにあたっての注意点
上記キャプチャにも書かれていますが、マイグレーションをすると .org の方の履歴は消えます。 Web config 部分も消えます。
マイグレーション依頼のメールを出すと、最初に本当にいいのかと?注意事項の確認がされます。

Migrating repos from travis.ci.org to travis-ci.com currently has the following caveats:

a) the build number will be reset to zero
b) the repo on travis-ci.com will start with a blank build history
c) the old build history will still be available on travis-ci.org
d) environment variables will not be migrated
e) environment variables will need to be recreated and, where necessary, re-encrypted by the customer for use in .travis.yml
f) repo settings such as enabled push or pull builds, auto-cancellation etc will not be migrated

iutest の場合、履歴に関しては、別に消えても特に問題がないので OK
Web config 系は cron 設定くらいなので、とりあえず .org の設定をキャプチャしておきました。



最初のビルドは4年以上前だったみたいですね。ほー

Hello Travis-CI ".COM"!
メールで注意事項に対して問題ないことを伝えると、ほどなく移行完了メールが来ると思います。
あとは、退避しておいた Web Config を戻すのと、 Encrypting environment variables を使っている場合は再生成が必要になります。
(再生成の際には、 travis login --pro で .com の方にログインして行います。エラーメッセージが出ると思うのでやってみればわかると思います)

これにて、無事に移行が完了しました。

GitHub Checks
さて、最後に一番の目的である GitHub Checks を見てみましょう。

https://github.com/srz-zumix/iutest/pull/118/checks


ちゃんと出てますね!
各 CI の結果が GitHub のページに集約されるのは、いろんなサービスを使っている自分としてはとても助かります。
対応サービスが今後も増えるといいですね。

今回は以上です。

2018年10月25日木曜日

iutest v1.16.7 をリリースしました

C++ テスティングフレームワーク iutest v1.16.7 をリリースしました。
Github: https://github.com/srz-zumix/iutest/releases
OSDN: https://osdn.net/projects/iutest/releases/70218

1.16 系は終わったと言ったな。。。あれはウソだ!

変更点は以下の通りです。
* Improved
  *  iuwandbox: コードサイズをさらに削減したヘッダーファイルに対応(--iutest-use-wandbox-min)

* Bug fixes
  * 古い google mock の互換性エラー修正
  * いくつかの不具合を修正

今回は主に、iuwandbox で利用する iutest.min.hpp のさらなる圧縮をした iutest.wandbox.min.hpp を使用するオプションを追加しました。
こちらの機能は試験的機能となっております。
問題なさそうであれば 1.17 系でデフォルトになる予定です。







もともと 600KB 近くあったのを、500KB 以下まで削減しました。
(↑は develop ブランチの計測なので、v1.16.7 だと少し違うかも。)

できあがった iutest.wandbox.min.hpp を見えもらえるとわかると思うのですが(Gist においておきました)、
iutest.min.hpp には、主に「構文の許す限りのインデントや改行削除」を行っていたのですが、iutest.wandbox.min.hpp では「内部マクロの名前省略」「Wandbox にない OS/Compiler 向け Config 削除」をしました。

iutest.min.hpp を作ったときの記事は以前書いているのでそちらも見ていただくとなにをしているかが、よりわかると思います。


今回の Wandbox 向けの圧縮はまた別途まとめて記事にしたいと思います。
(ローカル変数名とか、公開している部分でなければもっと名前の省略はできると思う)
(同一で連続したアクセス修飾子は消しても大丈夫なはずなので、そこも削れるはず)
(独自のリストクラスを使ってる部分をやめたらかなり減らせると思う。v1.17 でやる予定)

2018年10月15日月曜日

AppVeyor のジョブを1つのマトリックスにまとめました

AppVeyor は1つのリポジトリに対して、複数のプロジェクトを作成することができます。
GUI Config では複雑なマトリックスを構築できないですが、複数のプロジェクトを作成することによって、擬似的に複雑なマトリックスを組みことができてました。

これで十分ではあったのですが、 iutest で使用している CI サービスも増えてきて(現在 15 サービス超)、管理する場所をなるべく少なくしたいという点と、Webhook が足りなくなってきたという2点の理由から、なるべくまとめることとしました。
(1プロジェクトにつき、GitHub の Webhook を1つ消費する。GitHub の Webhook は1リポジトリにつき、20 まで)

複雑なマトリックスの鍵: for
通常マトリックスは matrix: で環境変数やワーカーを複数用意して、"同じ"ビルド処理を行います。
environment:
  matrix:
    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
      PROJECT_DIR: msvc12
      CMAKE_GENERATOR_NAME: Visual Studio 12 2013
    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
      PROJECT_DIR: msvc14
      CMAKE_GENERATOR_NAME: Visual Studio 14 2015
    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
      PROJECT_DIR: msvc15
      CMAKE_GENERATOR_NAME: Visual Studio 15 2017
    - BUILD_NUGET: yes

ただ、これでは同じリポジトリで、ビルド・テスト以外のリリース(デプロイ)ビルドや、全く別環境(Windows vs Ubuntu)でビルドフローが異なるような場合には対応できず、
iutest でももともとやっていたような複数プロジェクト化による対応が必要でした。
(設定をパラメタライズドにして状態の異なるビルド・テストはできる。処理が単一で設定でいろいろ複雑に処理を変えるのは面倒だった)

しかし、今回紹介する for を利用することで、任意のマトリックスに対して通常とは異なるビルドやテスト・デプロイを記述することができるようになります。
for の使い方はこちらです。 Build configuration | AppVeyor

上記のマトリックスは iutest で実際に利用している yml の抜粋になりますが、NuGet ビルドの特殊化の部分を例として以下に載せます。
for:
# NuGet
-
  matrix:
    only:
      - configuration: Release
        BUILD_NUGET: yes
  before_build:
    cmd: echo.
  build_script:
    ps: |
      $nuspecPath = "projects\nuget\iutest.nuspec"
      Write-Output "Building NuGet package"
      nuget pack $nuspecPath -OutputDirectory ".\"
  test: off
for の要素として、特殊化するマトリックスの条件を matrix に記述し、任意のステップを上書き設定します。

注意したいこと
for を使った特殊化は、通常フローの上書きになります。
特殊化するケースで使用しないステップ(未記載)でも、通常フローでそのステップを使用している場合はそちらが呼ばれてしまいます
そのため、何もしない場合でも、空の状態に上書きする必要があります。
上記例では、before_build と test のステップを空にしています。

YAML の構文チェック
さて、YAML Config は記述を間違うと、YAML 解析時に失敗してジョブが即失敗してしまいます。
なので、 push するまえに YAML の構文チェックをしておきましょう。

AppVeyor では以下のページで構文チェックができます。
構文がエラーがあると以下のように、エラーの箇所を教えてくれるので便利です。
https://ci.appveyor.com/tools/validate-yaml


特に問題がなければ、グリーン表示になります。


最後に
for でまとめた結果、複数に分かれていたプロジェクトの統一ができました。(全部じゃないけど)

for を使った高度なマトリックスは本家ブログでも紹介されています。
Advanced build matrix configuration in AppVeyor | AppVeyor
詳細は、公式ドキュメントや上記を参考にすると良いと思います。

では。

2018年9月19日水曜日

iutest v1.16.6 をリリースしました

C++ テスティングフレームワーク iutest v1.16.6 をリリースしました。
Github: https://github.com/srz-zumix/iutest/releases
OSDN: https://osdn.net/projects/iutest/releases/70056

変更点は以下の通りです。
* Improved
  * Matcher に (NanSensitive)FloatNear/DoubleNear を追加
  * Matcher に template 版の浮動小数点数比較を追加・(NanSensitive)FloatingPointEq/FloatingPointNear
  * long double に対応
  
* Changes
  * iuwandbox: -D__WANDBOX__ コンパイラーオプションを必ずつけるように変更

* Bug fixes
  * iuwandbox: dryrun オプションが失敗を返す問題を修正
  * いくつかのコンパイラー警告を修正

さて、1.16 系は今回で最後のリリースとなります。(なんもなければ…)
先行で対応開始していた C++17 対応を develop に取り込み、version 1.17 を目指して開発して参りますmm

2018年8月6日月曜日

iutest v1.16.5 をリリースしました

C++ テスティングフレームワーク iutest v1.16.5 をリリースしました。
Github: https://github.com/srz-zumix/iutest/releases
OSDN: https://osdn.net/projects/iutest/releases/69879

変更点は以下の通りです。
* Improved
  * IUTEST_*_NULL, IUTEST_*_NOTNULL を可変長引数対応
  * IUTEST_*_HRESULT_SUCCEEDED, IUTEST_*_HRESULT_FAILED を可変長引数対応
  * compatibility: iuutil に ad_hoc_testresult の取得関数を追加

* Changes
  * ad_hoc_testresult の取得関数名を ad_hoc_test_result に変更

* Bug fixes
  * iuwandbox: iuwandbox で std-cxx の指定をするとオプションチェックで失敗する不具合を修正

iuwandbox でコンパイラーの std オプションを指定すると、不明なオプションとなってしまう不具合を修正してます。
致命的な不具合だったので、早めのリリースとなりました。
(C++17 対応に着手しだしたので二桁になる前に 1.17 にしたい)

2018年7月24日火曜日

osdn-cli でパッケージリリース

前置き
本当は CI サービスを使った自動パッケージリリースをやろうと思っていたのですが、最初に試した osdn-cli がローカルマシンで使う想定だったため、実現できませんでした。
CI サービスなどからリリースする場合は、ファイルリリース機能を使うのではなく、ファイルストレージ機能を使うといいみたいです。


できあがったもの
なにはともあれ、osdn-cli を使ってパッケージリリースする方法は習得できたので、もったいないのでまとめました。




パッケージの作成
パッケージはすでに作成済みの前提です。
基本的にパッケージは一度作成したら変えることがないと思いますので、自動化から除外してます。

一応、
作成する場合は、 osdn package create で作成することができます。
既存かどうか判断しないと増え続けるので注意が必要です。
この辺はリリースの作成と同じような手順で対応できると思います。

リリースの作成
パッケージにリリースを作成します。以下のコマンドで作成しますが、まだファイルは一切アップロードしてない状態です。また、公開タイミングは手動で行うので、-v hidden オプションで非公開にしています。
osdn release create -v hidden --package $PACKAGE_ID $RELEASE_NAME

パッケージIDの取得
リリースを作成するときにパッケージの指定が必要になりますが、こちらは id 指定なので名前を指定してもダメです。
ID をベタ書きしてもいいのですが、汎用的に以下のコマンドで取得しました。
PACKAGE_ID=`osdn package | grep -o '[0-9]* iutest ' | cut -d ' ' -f 1`
パッケージのリストを出力、#ID 名前で出力されるので grep して ID を抽出してます。

リリースIDの取得
osdn release create では同名のリリースが既存であろうとなかろうと関係なくリリースを作成します。
そのため、すでに同名のリリースがあった場合は作成をスキップし、リリースIDの取得のみを行います。
リリースIDの取得は省略します。だいたいパッケージIDの取得と同じです。

最初に同名リリースを検索し、なければ osdn release create を行います。
その後リリースIDを取得しますが、これはファイルアップロードする際に利用するためです。

ファイルアップロード
osdn frs_upload --package $PACKAGE_ID --release $RELEASE_ID $FRS_RELEASE_ROOT
ファイルのアップロードは frs_upload を使いました。
パッケージとリリースIDを指定し、アップロードしたいディレクトリルートを渡したら OK です。
iutest のスクリプトでは、frs_mkdirs で各リリースのディレクトリを構築して、そこにリリースするファイルをコピーして、それをアップロードする形にしました。

アップロード後の作業
リリースノート・変更履歴の設定
上記リリーススクリプトで、リリースノートと変更履歴のファイルもアップロードするようにしています。
RELEASENOTE や CHANGELOG といった予め決められたファイル名のファイルを認識してくれる機能を利用しています。
ファイルリリースガイド - OSDN ヘルプ - OSDNドキュメント管理 - OSDN

ただ、このファイルをアップロードしてもそれだけではリリースノート・変更履歴として、表示されませんでした。これらのファイルを表示させるには、リリースの編集からチェックを変更して保存する必要がありました。


これ、ちょっと面倒ですね。。。


あ、あと、リリースノートとか変更履歴のファイルは非公開にすると、当然のようにリリースに表示されなくなります。
(別にファイルとしてダウンロードさせたいわけじゃないので、ファイルは非公開だけどリリースには表示されると嬉しかった)
ステータスを公開に変更する
リリース作成・ファイルアップロードを自動化しましたが、公開状態にするのは手動でやるように あえて したので、公開準備ができたらリリースページに移動して、ステータスを公開にします。



これで、リリース作業が完了しました。

最後に
デプロイを自動化、 CI サービスで動かして完全自動化といきたいとこでしたが、
それでも今まで手動で AppVeyor からパッケージダウンロードして、手動で OSDN のリリースを作成していたのを考えれば大分楽になりました。
引き続き、このスクリプトは利用しますが、ファイルストレージ機能を使ったデプロイになる早で移行したいなーと思います。

ではでは。

2018年7月5日木曜日

iutest v1.16.4 をリリースしました

C++ テスティングフレームワーク iutest v1.16.4 をリリースしました。
Github: https://github.com/srz-zumix/iutest/releases
OSDN: https://osdn.net/projects/iutest/releases/69788

変更点は以下の通りです。
  • 変更
    • deprecated: IUTEST_IGNORE 関係を非推奨にしました
  • 修正
    • バグ修正

今回も大きな機能リリースはなしです。
ただ大きな変更として、ファイルの改行コードが半端に混在している状態だったのを正常化しました。
また、一部のコンパイラーでの警告対応が入ってます。

今回は yumetodo san にとってもお世話になりました。
ありがとうございましたmm

2018年6月12日火曜日

iutest を CMake のパッケージ検索に対応させた

issue の方で提案頂いたので対応しました。
https://github.com/srz-zumix/iutest/issues/40
といっても、私はほとんど何もしてません。
issue に貼っていただいたテキストをコピペした程度です。(yumetodo san ありがとうございますm(__)m)


この記事はもう少し続きますが、今回は(も?)まとまりのない駄文ですので、はい。
きれいにまとまったときにちゃんと読んで頂ければ大丈夫です…








そもそも私、find_package を知りませんでした。
まずはそこからお勉強しました。

参考にしたところ
find_packageの動作 - Qiita
CMakeを使ってみた (7) find_packageとpkg_check_modulesによるライブラリ探索 - wagavulin's blog

依存パッケージを探してくれるもので、見つけたパッケージのインクルードパスとかライブラリパスとかが取れるみたいですね。
で、その探索に使われるのが、FindXXXX.cmake だったり、cmake/xxxx-config.cmake だったり、cmake/XXXXConfig.cmake だったりするわけですね。
(Find の方は非推奨?っぽいですが)
CMake のことにまた少し詳しくなった気がしました。




しかしながら、これを提供する側はどうするのがいいのか?というのがイマイチわからず。
とりあえず、ルートに cmake フォルダを掘って配置しましたが、これでいいのだろうか?
そもそも CMake でサンプル/テストビルドを作るのは用意して深めのディレクトリに置いてましたが、これもルート直下に CMakeLists.txt を置くのが普通なのかな?

圧倒的 CMake 力不足・・・
もうちょっと情報集めて整理したいですね・・・



ここまでお付き合い頂き、ありがとうございます。
なんだか申し訳ない気持ちでいっぱいですが、ここまでお付き合い頂いたついでに・・・
なにか参考になるページや情報をお持ちでしたら、コメント頂けると嬉しいです。

では。m(__)m

2018年5月2日水曜日

iutest v1.16.3 をリリースしました

C++ テスティングフレームワーク iutest v1.16.3 をリリースしました。
Github: https://github.com/srz-zumix/iutest/releases
OSDN: https://osdn.net/projects/iutest/releases/69492

変更点は以下の通りです。
  • 追加
    • IUTEST_ASSERT,IUTEST_EXPECT の Variadic 対応
    • iuwandbox: --iutest-use-main を追加
  • 変更
    • iuwandbox: Wandbox の std オプション変更への追従とヒント出力
  • 修正
    • バグ修正

今回も大きな機能リリースはなしです。
(単一マクロ引数だったアサーションを Variadic 対応している途中なのでその紹介は次回リリース時にでも…)
C++17 的な機能を実装して 1.17 リリースしたいなーと思ってるので、しばらく 1.16 系が続くかも…

2017年12月29日金曜日

iutest v1.16.2 をリリースしました

C++ テスティングフレームワーク iutest v1.16.2 をリリースしました。
Github: https://github.com/srz-zumix/iutest/releases
OSDN: https://osdn.jp/projects/iutest/releases/68841

とても久しぶりのリリースですが、特に変わってません・・・
最近は CI 周りばかりいじっていて進捗が・・・
あんまりキリがいい状態ではなかったのですが、さすがに年内にはもう一本リリースしないとなーということで出しました。はい。

変更点は以下の通りです。
  • 追加
    • 警告をエラーとするオプション(--iutest_warning_into_error)を追加
  • 削除
    • NaCl サポートを終了
  • 修正
    • バグ修正

来年は機能開発したいですね。では。

2017年8月8日火曜日

[CI] Scrutinizer 始めました



今回紹介する CI サービスは Scrutinizer です。
Pricing をみるとフリープランはないように見えますが、OSS であればフリーで使えるようです

今回、使い始めてから紹介できるようになるまでにかなーりの年月を費やしてしまいましたので、Sign up まわりは省略させていただきます。
(C++非サポート&Python のテスト周りで手間取った)

Scruntinizer の魅力
Scruntinizer の魅力は登録するだけですぐに静的解析の結果が得られるところです。
Scruntinizer は静的解析などの検証に特化した CI サービスで、めんどうな設定や YAML を書かずにすぐに始められます。テストもリポジトリを探索して自動で実行してくれます。

あと、レポートもグラフィカルでいい感じです。


使用例
いつもであれば、iutest の使用例で紹介するのですが、C++ は非サポートなので、
今回は「ブログズミ: ソースコード中の単語からの略語/スペルミス検出に挑戦」で作成した
taggertool リポジトリで紹介します。

リポジトリ登録
まずは、リポジトリの登録から始めます。
「Add Repository」ボタンを押すと、以下のような画面になるので、CI したいリポジトリを入力、言語を選択して、登録します。


リポジトリは、Github/Bitbucket/GitLab/PlainGit から選択できます。
Plain Git が可能なので、独自リポジトリが使えるのは良いところだと思います。

登録は以上です。
完了すると最初のテストが実行されます。

taggertool の結果
taggertool の結果はこんな感じです。

サマリー


Issues に、各静的解析で出た結果が出ます。


Code にはクラスおよび関数のサイズと複雑度がリストアップされます。


Inspections はキックされたジョブが並びます。


Reports には最後に実行されたジョブの結果が表示されます。


最後に、Statistics には Quality Rating や issue などの推移がグラフで確認できます。
Quality Rating は上の図で 9 となっているやつで、コードの複雑度、結合度など複数の要素から1つのスコアにしたもので、10点満点で評価されます。


チェック項目を増やす
さて、Scrutinizer には様々なツールで様々な解析ができる準備がされていますが、デフォルトでは多くのチェックは無効になっています。
チェックを有効にするには、Settings の Configuration に Checks ボタンがあるので、そこを開きます。


開くと Not Enabled なチェック項目がリストアップされているので、
必要なものにチェックを入れてください。


チェックの有効化は Congiguration の Repogitry Config や yml に直接書くこともできます。
チェックしたい項目が多い場合は、(UI でチェックするのが面倒なので)こちらの方がよいでしょう。

また、グローバルな設定も作成できるので、
複数プロジェクトで同じチェックをさせたい場合は、そちらを使うと良さそうです。
checks:
    python:
        code_rating: true
        duplicate_code: true
        variables_used_before_assignment: true
        variables_unused_wildcard_import: true
        variables_unused_variable: true
        variables_unused_import: true
        variables_unused_argument: true
        variables_unpacking_non_sequence: true
        variables_undefined_variable: true
        variables_undefined_loop_variable: true
        variables_undefined_all_variable: true
        variables_unbalanced_tuple_unpacking: true
        variables_redefined_outer_name: true
        variables_redefined_builtin: true
        variables_redefine_in_handler: true
        variables_no_name_in_module: true
        variables_invalid_all_object: true
        variables_global_variable_undefined: true
        variables_global_variable_not_assigned: true
        variables_global_statement: true
        variables_global_at_module_level: true
        typecheck_unexpected_keyword_arg: true
        typecheck_too_many_function_args: true
        typecheck_redundant_keyword_arg: true
        typecheck_not_callable: true
        typecheck_no_value_for_parameter: true
        typecheck_no_member: true
        typecheck_missing_kwoa: true
        typecheck_maybe_no_member: true
        typecheck_duplicate_keyword_arg: true
        typecheck_assignment_from_none: true
        typecheck_assignment_from_no_return: true
        string_truncated_format_string: true
        string_unused_format_string_key: true
        string_too_many_format_args: true
        string_too_few_format_args: true
        string_mixed_format_string: true
        string_missing_format_string_key: true
        string_format_needs_mapping: true
        string_constant_anomalous_unicode_escape_in_string: true
        string_bad_str_strip_call: true
        string_constant_anomalous_backslash_in_string: true
        string_bad_format_string_key: true
        string_bad_format_character: true
        open_mode_bad_open_mode: true
        miscellaneous_fixme: true
        newstyle_bad_super_call: true
        logging_unsupported_format: true
        logging_too_many_args: true
        logging_too_few_args: true
        logging_not_lazy: true
        logging_format_truncated: true
        imports_wildcard_import: true
        imports_relative_import: true
        imports_reimported: true
        imports_import_self: true
        imports_import_error: true
        imports_deprecated_module: true
        imports_cyclic_import: true
        format_unnecessary_semicolon: true
        format_trailing_whitespace: true
        format_superfluous_parens: true
        format_old_ne_operator: true
        format_multiple_statements: true
        format_mixed_indentation: true
        format_missing_final_newline: true
        format_lowercase_l_suffix: true
        format_line_too_long:
            max_length: '100'
        format_bad_whitespace: true
        format_bad_indentation:
            indentation: '4 spaces'
        format_backtick: true
        exceptions_raising_non_exception: true
        exceptions_raising_string: true
        exceptions_raising_bad_type: true
        exceptions_pointless_except: true
        exceptions_notimplemented_raised: true
        exceptions_catching_non_exception: true
        exceptions_broad_except: true
        exceptions_binary_op_exception: true
        exceptions_bare_except: true
        exceptions_bad_except_order: true
        design_interface_not_implemented: true
        design_abstract_class_not_used: true
        design_abstract_class_little_used: true
        classes_valid_slots: true
        classes_super_init_not_called: true
        classes_signature_differs: true
        classes_protected_access: true
        classes_non_parent_init_called: true
        classes_non_iterator_returned: true
        classes_no_self_use: true
        classes_no_self_argument: true
        classes_no_method_argument: true
        classes_no_init: true
        classes_missing_interface_method: true
        classes_method_hidden: true
        classes_interface_is_not_class: true
        classes_bad_staticmethod_argument: true
        classes_bad_mcs_method_argument: true
        classes_bad_mcs_classmethod_argument: true
        classes_bad_context_manager: true
        classes_bad_classmethod_argument: true
        classes_attribute_defined_outside_init: true
        classes_arguments_differ: true
        classes_access_member_before_definition: true
        classes_abstract_method: true
        basic_yield_outside_function: true
        basic_useless_else_on_loop: true
        basic_unreachable: true
        basic_unnecessary_pass: true
        basic_unnecessary_lambda: true
        basic_star_args: true
        basic_return_outside_function: true
        basic_return_in_init: true
        basic_return_arg_in_generator: true
        basic_pointless_string_statement: true
        basic_pointless_statement: true
        basic_old_raise_syntax: true
        basic_not_in_loop: true
        basic_nonexistent_operator: true
        basic_missing_reversed_argument: true
        basic_missing_module_attribute: true
        basic_missing_docstring: true
        basic_lost_exception: true
        basic_init_is_generator: true
        basic_function_redefined: true
        basic_expression_not_assigned: true
        basic_exec_used: true
        basic_eval_used: true
        basic_empty_docstring: true
        basic_duplicate_key: true
        basic_duplicate_argument_name: true
        basic_dangerous_default_value: true
        basic_bad_reversed_sequence: true
        basic_assert_on_tuple: true
        basic_abstract_class_instantiated: true

最後に
Scruntinizer は、最初から静的解析ツールが用意されており、それを目的にするのであれば、
通常の CI サービスよりも、とても簡単に恩恵が得られるのでオススメです。
(C++ がサポートされるともっとステキですが…あ、あと Scruntinizer のスペルが全然覚えられない…

今回は Python で利用しましたが、他の言語で何か作るときは、また利用したいなと思います。

以上。