技術書典5「クロス本」売り上げ報告ほか

この記事は技術同人誌 その2 Advent Calendar 2018 Advent Calendar 2018 の17日の記事です。




以上、売り上げ報告でした。
以下雑文。



本書、タイトルが長いので「クロス本」と呼んでます。以下ではそう書きます。
...本当は、クロス本執筆の動機を説明するのにちょうどよいため本文から「はじめに」章を転載しようと考えていたのですが、もしかすると参加アドベントカレンダーのレーティングに抵触する可能性が。わたしはGTK大好きです本当なんです。でなきゃアプリ開発に2年も投入しないし本も書かない。信じて。
そんな殺伐ポエム4Pを含むけれど他のページは本当に実用的な知見の塊「クロス本」は、電子書籍版をPixiv/BOOTHgubloadで買えます。






以下、気持ち。




だいたいの時系列。
- 参加を決めたのはサークル募集が始まってから。
- 何か新しい開発をする+それを書く、はつらい。既にやったことを書くほうが執筆時コストが少なく文章へ割けるリソースが増える。(思い出すというコスト、思い出せないというリスクは有る。)




- 内容は早くに時系列エッセイからTIPS集へ。そのほうが前後関係という依存関係が減り章がモジュール化されるため、書くのが楽になる。
- 原稿はMarkdownで書いてpandocベースの自作コンバータでPDF化。







- 入稿が初めてで一番安い締め切りは逃した。
- 日本語リンタは今回のクロス本の執筆には役立たなかった。
- 知り合いに原稿データを配って試し読みしてもらった。感謝。
- Web上での試し読み募集には応募がなかった。自分は別の方の本のレビューに参加させていただいた。
- チェック数と売上データのグラフを作っている方がいて、とても参考になったので自分もチェック数と売り上げを公開した。





こんなこともしていました。あまり反響無かったですが。



敵に囲まれておる、と思った。



- 確かサークル一覧公開から5日後くらいに、「この時期のブックマーク数の3倍の冊数が売れます」って言っている人がいたが、自分の場合は実際にそうなった。
- 売り子の約束をしていた友人が所要で1ヶ月前に自分1人で参加することになった。
- 別の知人が買い物ついでにお昼頃の店番をしてくれることになったので、買い物はその間にさせてもらった。

初めての技術同人誌、そして物理書籍の物理物販は、自分にとって大きな挑戦の機会でした。
またリリースされたクロス本は、今年の主要で輝かしい「進捗」となりました。

ことのはアムリラートには感謝してます。

今年は主要な進捗としてクロス本の他に、「daisy-cell-block-editor」をリリースしたいと考えています。
クロスプラットフォーム・デスクトップ・アプリケーションを扱ったクロス本に登場したクロスプラットフォームUMLシーケンス図エディタdaisy-sequenceの姉妹アプリケーションです。当然クロスプラットフォーム。


ご期待下さい。

seleniumテストの失敗、デバッグと引数と解決

seleniumテストが失敗する問題のデバッグと解決
====

# TL;DR
関数名のtypoを修正すると、それまで通っていたテストが落ちる。
原因は、テストシステムが生成して渡した不正な引数による異常系終了だった。
対処として、テストケース実行を汚い実装で検出して特別に処理することで問題を回避することとした。


# 問題
## 詳細
electron製アプリのdaisy-sequenceに対して、selenium/mochaでウィンドウの起動テストを行っている。
これはほぼselenium公式サンプルと同じシンプルなテストである。

daisy-sequenceではコマンドライン引数を受け取る。
コマンドライン引数のパースは`js/cli.js` の `class Cli.parse(argv)`にて行っている。
この中で、`sprintf-js`パッケージの`sprintf()`関数をtypoしていた。
```js
        const sprintf = require('sprintf-js').sprintf;
(略)
        if(2 < argv.length){
            if(argv[2] !== '-o'){
                arg.err = {
                    'message': spirntf('invalid argment. `%s`', argv[2])
                };
                return arg;
            }

```

これを修正したところ、ウィンドウが即座に終了し、seleniumテストに失敗する。
```bash
make unit-test
make[1]: ディレクトリ '/home/nuka/daisy_sequence' に入ります
cd daisy_sequence && npm run test test/

> daisy_sequence@201812.2.0 test /home/nuka/daisy_sequence/daisy_sequence
> mocha --require intelli-espower-loader "test/"



  Array
    #indexOf()
      ✓ should return -1 when the value is not present

  Application launch
    1) shows an initial window
    2) "after each" hook for "shows an initial window"


  1 passing (3s)
  2 failing

  1) Application launch
       shows an initial window:
     chrome not reachable
  Error: Request timed out after the element was still found on the page.
      at windowHandles() - application.js:240:17

  2) Application launch
       "after each" hook for "shows an initial window":
     chrome not reachable
  Error: Request timed out after the element was still found on the page.
      at windowHandles() - application.js:246:17



^CTerminated
Makefile:20: recipe for target 'unit-test' failed
```

# デバッグ

前提として、typoを修正するとテストに失敗する。
別のtypoを入れる、alart()を挿入する等すると、テストに成功するようになる。 // alart()を入れるのは不正なのか?
 > これまでテスト失敗が起こらなかったのは、不正で止まることが原因だったらしい。
arg.errへの代入(arg.errはエラーフラグを兼ねる)を消すとテストに成功する。
 > arg.errへの代入がおかしい? それとも、parse()がエラーを返すと失敗する?
Cli.parse()エラー時の`process.exit(-1);`を消すと、テストに成功する。
 > parse()がエラーを返すとテストが失敗する!
ところでprocess.exit()するとウィンドウが即座に閉じる。ウィンドウが閉じればテストは失敗する。
つまり不正な引数を受け取っていることが原因の可能性がある。

引数を確認する。
console.error()系, process.write.stderr()等を使用しても出力されない。
// 上記デバッグの際もこれが作業の困難さを増した。
対処として、単にファイルに書き出すこととした。
`fs.writeFileSync("daisy-arg", JSON.stringify(remote.process.argv));`
結果、取得できたselenium/mocha実行時のコマンドライン引数。
```js
["/home/nuka/daisy_sequence/daisy_sequence/node_modules/electron/dist/electron","/home/nuka/daisy_sequence/daisy_sequence","--disable-background-networking","--disable-client-side-phishing-detection","--disable-default-apps","--disable-hang-monitor","--disable-popup-blocking","--disable-prompt-on-repost","--disable-sync","--disable-web-resources","--enable-automation","--enable-logging","--force-fieldtrials=SiteIsolationExtensions/Control","--ignore-certificate-errors","--load-extension=/tmp/.org.chromium.Chromium.oi97WO/internal","--log-level=0","--metrics-recording-only","--no-first-run","--password-store=basic","--remote-debugging-port=12106","--test-type=webdriver","--use-mock-keychain","--user-data-dir=/tmp/.org.chromium.Chromium.h85Kss","data:,"]
```
daisy-sequenceは、第二引数が存在する場合に`-o`でなければ、不正なコマンドライン引数として即座に失敗終了する。
(ただし、先頭がelectronで終了する場合これを引数に数えない。)

原因は不正な引数を受け取っていたことによる不正終了により、ウィンドウが即時終了していたことであると考えられる。

## 現象
大筋は以下。
typo修正前の動作はこのようになっていた。
- selenium/mocha実行はelectronに対してテストに適したコマンドライン引数を渡し、アプリケーションはそれを受け取る。
- アプリケーションはtypoにより「未知の関数を実行しようとした例外」で停止する。
- selenium/mocha実行は途中でアプリケーション側に例外が発生しても検知(失敗判定)しない。
- seleniumはウィンドウが起動している・終了できたことに満足してテストをOK判定する。
以上のことからseleniumによるウィンドウ起動テストは成功していた。
なお、「不正な引数を受け取ったことによる不正終了」と「未知の関数を実行しようとした例外」はどちらもアプリケーションの不正終了を起こす。他のテストでは不正な引数をわたしたことによる不正終了が起こりその理由の区別はつかないので、テスト成功となっていた。

typoを修正したことで、以下のように変化した。
- selenium/mocha実行時のコマンドライン引数を受け取ることで、アプリケーションは不正な引数エラーで即座に不正終了する。
- seleniumはウィンドウが起動していないためテストをNG判定する。

// ところで「不正終了」という言葉の使い方が正しくない気がする。まあいいけど。

# 対処
コマンドライン引数からそれらしい引数を検出したらselenium実行と判定してコマンドライン処理をskipすることとした。
キー文字列は"--test-type="にした。

# 今後の課題
- selenium/mocha実行は途中でアプリケーション側に例外が発生しても検知(失敗判定)しないが、検出して失敗判定するようにしたい。
 daisy-sequenceは幸い、正常系で(起動時にwarningが一件出るのと、編集削除操作でエラーログを出力する以外に)例外が発生しない。
- アプリケーション側でパースする際にelectron向けコマンドライン引数を適切に排除する。

electronアプリnode cliアプリでのコマンドライン実行の種類とparseの共用について

必要なelectron,nodeのCLIについての周辺情報の収集・整理を行った。
(CLIというとユーザ入力のプロンプトを含むが、今回はプロンプトを含まないコマンドライン引数によるワンショット実行をCLIと呼びそれを扱う。)

# 要求仕様
daisy-sequenceおよびdaisy-cell-block-diagram(以下daisyシリーズ)は本物のシステム開発アプリケーションを志向している。
本物のシステム開発アプリケーションはCLI、CI連携可能なコマンドラインインタフェースを持つ。
本稿ではdaisyシリーズのCLIについて、以下の通り仕様と実装を検討し、必要なelectron,nodeのCLIについての周辺情報の収集・整理を行った。


## 画面仕様
usage: `app [input_filepath [-o output_filepath]]`
- guiとcli(コマンドライン)を提供する。コマンドラインインタフェースを共通とする。
    - GUI,CLIの区別は別バイナリ提供により実現する。
    - output指定の有無によるGUIとCLIそれぞれの挙動の違いについては未規定とする。
    - (GUIではoutput指定があると不正終了または書き出して終了、CLIではoutputが無ければ不正終了、を想定している。)
- helpとversionはあるに越したことはないが無くていい。
- win/mac/linux対応。
    - win,linuxのランチャに対応するため、GUIのオプション第一引数は渡されたファイルを開く。

## 機能仕様
- electronとnode(cli)で可能な限りコードを共有したい。せめて設定パラメタは共有したい。

# electorn, nodeのCLIの基礎
一般的なコマンドラインでは、先頭がアプリケーション実行ファイルの起動パスで後ろにコマンドライン引数が付く形で実行し、アプリに渡されるコマンドライン引数(argv)も同じである。
しかし、node, electronではそうでない場合がある。

### nodeから起動する場合と、npmパッケージのCLIコマンドとして実行する場合では引数の数が変わる
1. `node ./path/myapp.js myarg1 myarg2`
2. `npm run myapp -- myarg1 myarg2`
3. `node_modules/.bin/myapp`


`node`コマンドによる実行では、頭に`node`が付く。開発中などに用いる。
package.jsonコマンドによる`npm run`実行では、一般的なコマンドライン引数が渡される。
(`--`を付けることで以降の引数が`npm run`から実行アプリケーションに渡る。
 [npm run (npm run-script) コマンドで実行するスクリプトに引数を渡す。2017]( https://qiita.com/tiny-studio/items/ce28bf84c76aba53122f ) )
package.jsonのbin要素による実行ファイル実行では、一般的なコマンドライン引数が渡される。npm配布パッケージとしてインストールした後などに用いる。

```js
 10 >-------// コマンドライン引数を[1]からに調整(node実行の場合に必要)¬
 11 >-------let argv = process.argv;¬
 12 >-------if(argv[0].endsWith('node')){¬
 13 >------->-------argv.shift();¬
 14 >-------}¬
```

### electronで起動する場合と、ビルド後のアプリでは引数の数が変わる
1. `npm run myapp myarg1 myarg2`
2. `./myapp.exe myarg1 myarg2`


package.jsonコマンド起動では、頭に`electron .`と引数2個が付く。electronで起動するためである。開発中などに用いる。
ビルド後のアプリケーションでの実行は、一般的なコマンドライン引数が渡される。

```js
  63 >-------let argv = remote.process.argv;¬
  64 >-------if(argv[0].endsWith('electron') && argv[1] === '.'){¬
  65 >------->-------argv.shift(), argv.shift();¬
  66 >-------}else{¬
  67 >------->-------argv.shift();¬
  68 >-------}¬
```

### (備考) macosxの場合
本編ではないが。
macosxでファイルタイプに対するデフォルト起動アプリケーション登録によるファイルOpen(winのexplorerやLinuxのランチャ)は第一引数によらない。
`open-file`eventで受ける。念の為。
```js
app.on('open-file', openFileHandler);
```

### コマンドラインパーサの呼び出し位置
nodeのコマンドラインパーサパッケージを使う場合、そのうちのいくつかは、electronの'ready'イベントの前にMainProcessで使用する必要があると思われる。
基本的に、electronのRenderProcess側のわりと特殊な環境を想定していない模様。
具体的にはargsをremoteから取る部分、console.log()出力など。

[electronにおけるコマンドライン引数の位置とパーサ]( https://qiita.com/akameco/items/cfbde2f1b241e2babc6c )

command-line-args を使ってエラーで失敗していたのだが、RenderProcess側でやっていたのが問題であった可能性がある。

ただし、後述の通りglobalな変数を使用せずアプリケーションからargvを渡す形のインタフェースであればこれは問題とならないし、daisyシリーズではこれを採用する。


# 再度、要求仕様
- node,electronで共用するにあたり、node,electronおよび呼び出し方法によるCLIの違いを吸収できる必要がある。
  具体的には、これら先頭オプションの違いを取り除く前処理を行ったargvを渡せる必要がある。
  (例えば`argv`パッケージはargv引数を取らないので落選となる。)

minimistを用いるか、規模的に独自実装するのが適切と考える。


# vscodeの場合
vscodeではmain.jsでminimistをめちゃくちゃ単純なオプションで使用している。
後処理でもっと複雑なことをしているような気もするが、調査していない。

```js
/**
 * @returns {ParsedArgs}
 */
function parseCLIArgs() {
    const minimist = require('minimist');

    return minimist(process.argv, {
        string: [
            'user-data-dir',
            'locale',
            'js-flags',
            'max-memory'
        ]
    });
}
```

## 引き渡し
MainProcessからRenderProcessへの引き渡しは、ShareObjectを使えばよさそう。

main.js
```js
  8 global.sharedObject = {'osx_open_file': null};¬
  9 let openFileHandler = function(event, path) {¬
 10 >-------event.preventDefault();¬
 11 >-------global.sharedObject.osx_open_file = path;¬
 12 };¬
```

js/index.js
```js
  85 >-------if(typeof remote.getGlobal('sharedObject').osx_open_file === 'string'){¬
  86 >------->-------if(null === arg.open_filepath){¬
  87 >------->------->-------arg.open_filepath = remote.getGlobal('sharedObject').osx_open_file;¬
  88 >------->-------}¬
  89 >-------}¬
```

## 先頭ファイルパスの実現
minimistは本当にコマンドラインパーサをするだけ。help(usage)は作ってくれない。
よって、先頭ファイルパスを除いてそれより後ろのargvを渡せばよい。`argv.shift()`よりも`argv.slice(2)`がよくサンプルコードに書かれる。


electronアプリをbrowserifyしてAndroid移植したmemo

現実世界のelectronでnodejsな趣味コードをbrowserifyした際の知見。
====

世の中のまともなプロjsエンジニアは最初からボイラープレートでgulpなビルドパイプラインを構築してまっさらなプロジェクトにjsxコードをおもむろに書き始めるとかしているのだと思います。
しかし現実のワークフロー、というか開発フロー、もとい移植フローというのはそんなに綺麗なものではありません。
例えばelectronのためにnode.js的なmodule.exportsで書いていた移植性を考えていないjsコードをどうにかブラウザもといWebViewで動かしたい向きもあるわけです。
つまり私です。個人プロジェクトなんですよlina_dictoは。
(あ、移植性はちょっと考慮してた。そこは嘘になっていますが嘘でなくて機能追加の際に一度移植性について諦めたのです。)





成果物:
https://github.com/MichinariNukazawa/lina_dicto_for_android_2018


# 前置き
electronで書いたエスペラント辞書アプリケーション lina_dicto をAndroidのWebViewに移植したのが lina_dicto_for_android です。
以前は、
- electron側のコードを出来るだけ、require()などのnode固有機能を使わない、ピュアなブラウザjsで書く
- ファイルIOなどどうしてもブラウザで出来ない部分はMakefileによるビルドプロセスでファイル上書き・置換等を行う
など、ある程度移植を意識したelectronコードを書くことで対処してきました。
しかしunittestにワンクッション入れたり、機能追加が面倒だったりしたため、lina_dicto(electronデスクトップ版)はこの前の機能追加の際にこの方針を諦めました。
そのためAndroid版の更新追従は諦めなければならなかったのですが、そうは言っても新機能は魅力的で、Android版での提供を諦めきれるものではありませんでした。
というわけで、electron向けコードをbrowserifyでブラウザjsに変換する方向で対処してみた次第です。

## browserifyがlocalなnodejsモジュールを読み込まない

lina_dictoのディレクトリ構成は下記の通り。require()さえ解決すればブラウザ実行できてしまう構成になっています。
```
lina_dicto/
    index.js        # electronのエントリポイント
    index.html      # メインページ(ピュアHTML)
    js/
        index.js    # 辞書アプリケーションのエントリポイント(HTMLページに読み込まれる)
        *.js
        ...         # その他jsファイル(scriptタグで読まれるブラウザjsやrequire()で読み込むmodule.exportsモジュールが混在)
```

lina_dictoは js/index.js が主要機能のエントリポイントで、以下の通りrequire()で`module.exports`を使ったローカルなjsファイルのモジュールを読み込んでいます。
```js
  1 'use strict';¬
  2 ¬
  3 var extension = new Extension();¬
  4 var platform = new Platform();¬
  5 // const Language = new Language();¬
  6 const Language = require('./js/language');¬
  7 const Esperanto = require('./js/esperanto');¬
  8 const Dictionary = require('./js/dictionary');¬
  9 let dictionary = new Dictionary();¬
 10 const Linad = require('./js/linad');¬
 11 let history = new History();¬
 12 ¬
 13 var timeline_item_id = 0;¬
 14 let dictionary_handle = null;¬
 15 ¬
 16 window.onload = function(e){¬
 17 ¬

```

しかしこれをこのようにbrowserify にかけると、
`node ./node_modules/browserify/bin/cmd.js js/index.js -o bundle.js`
下記エラーが出ます。

```js
lina_dicto/$ node ./node_modules/browserify/bin/cmd.js js/index.js -o bundle.js
Error: Cannot find module './js/dictionary' from '/home/nuka/lina_dicto_for_android/app/src/main/assets/lina_dicto/js'
    at /home/nuka/lina_dicto_for_android/app/src/main/assets/lina_dicto/node_modules/browser-resolve/node_modules/resolve/lib/async.js:55:21
    at load (/home/nuka/lina_dicto_for_android/app/src/main/assets/lina_dicto/node_modules/browser-resolve/node_modules/resolve/lib/async.js:69:43)
    at onex (/home/nuka/lina_dicto_for_android/app/src/main/assets/lina_dicto/node_modules/browser-resolve/node_modules/resolve/lib/async.js:92:31)
    at /home/nuka/lina_dicto_for_android/app/src/main/assets/lina_dicto/node_modules/browser-resolve/node_modules/resolve/lib/async.js:22:47
    at FSReqWrap.oncomplete (fs.js:152:21)
```
悩ましいのが`Cannot find module `のmoduleがビルド毎にランダムに変わることです。わけがわからない。多分中で並列ビルドなりなんなりしているのだと思いますが。
ともあれ解決は以下のとおりです。

```
mv js/index.js .
node ./node_modules/browserify/bin/cmd.js index.js -o bundle.js
```
つまりは相対パスの解釈の問題らしいです。
これlina_dictoでは偶然これで解決できるパス構成だったからよかったものの、もっと複雑なパスを組んでるアプリケーションだったらパスの書き換えまで必要だったのでは?

## scriptタグで読んだブラウザjsコードから、browserifyしたfunctionを呼ぶことができない
早速ブラウザで実行すると、下記エラーで止まりました。
```js
platform.js:9 Uncaught ReferenceError: query_input_element is not defined
    at Platform.init (platform.js:9)
    at window.onload (bundle.js:26)
```

該当箇所のコード
```js
  3 class Platform{¬
  4 >-------init()¬
  5 >-------{¬
  6 >------->-------// 検索ボタンを有効化¬
  7 >------->-------let button = document.getElementById("query-area__query-input__button");¬
  8 ¬
  9 >------->-------button.addEventListener("click", query_input_element, false);¬
 10 ¬
 11 >------->-------return true;¬
 12 >-------}¬
```

nodeモジュール化して一緒にバンドルすれば解決するかとおもったのだが、ならなかった。
```js
bundle.js:2625 Uncaught ReferenceError: query_input_element is not defined
    at Function.init (bundle.js:2625)
    at window.onload (bundle.js:26)
```

仕方ないのでlina_dicto本体コードに直接追加した。
```js
   53 >------->-------{¬
   54 >------->------->-------// browserifyで別ファイルから関数を指定できない問題を、¬
   55 >------->------->-------// 解決するのが面倒だったため、ここへ処理を追加¬
   56 >------->------->-------if('android' === Platform.get_platform_name()){¬
   57 >------->------->------->-------// 検索ボタンを有効化¬
   58 >------->------->------->-------let button = document.getElementById("query-area__query-input__button");¬
   59 >------->------->------->-------button.addEventListener("click", query_input_element, false);¬
   60 >------->------->-------}¬
   61 >------->-------}¬
   62 >-------});¬
   63 }¬
```
lina_dicto本体コードに不要コードが増えたことになるが、コードの美しさがlin_dictoのゴールではない。この程度なら許容範囲とした。

## browserifyしたコードから、バイナリファイルを読み込めない

```js
bundle.js:10347 Access to XMLHttpRequest at 'file:///node_modules/kuromoji/dict/base.dat.gz' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
```

https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control/Errors/CORSRequestNotHttp?utm_campaign=default&utm_medium=firefox-cors-errors&utm_source=devtools

エラーチェックを緩和することで関係部分以外の基本的な辞書動作はブラウザで確認できる。そこから先をchromiumでのこれ以上の動作確認は諦めて、Androidでやることに。

Android WebViewではCORSを緩和できる。
```
        webView1.getSettings().setAllowFileAccessFromFileURLs(true);
```
https://developer.android.com/reference/android/webkit/WebSettings
https://stackoverflow.com/questions/4666098/why-does-android-aapt-remove-gz-file-extension-of-assets

Androidのassetから拡張子 .gz ファイルは取り除かれる。
これは拡張子を変えることで回避できる。
```bash
 cd node_modules/kuromoji/dict/
 rename 's/\.gz/.bin/' *
```
どうやら.gz.binでは駄目で、.gzを除く必要がある。


## HTMLから、browserifyしたfunctionを呼ぶことができない

イベントコールバックの登録に、HTMLへ書き込む古い方法を使っていました。
```html
    <input id="query-area__query-input__input" type="text"¬
        value='wakeup...' disabled='true'¬
        onkeypress="on_keypress_by_query_input_element(event);" onkeyup="on_keyup_by    _query_input_element(event);" />¬
```

しかしこれは下記エラーを起こす。
```js
index.html:27 Uncaught ReferenceError: on_keypress_by_query_input_element is not defined
    at HTMLInputElement.onkeypress (index.html:27)
onkeypress @ index.html:27
index.html:27 Uncaught ReferenceError: on_keyup_by_query_input_element is not defined
    at HTMLInputElement.onkeyup (index.html:27)
```

index.js(bundle.js)でaddEventListener()するよう書き換えて処置した。
```js
 52 >------->-------{¬
 53 >------->------->-------let input = document.getElementById("query-area__query-input__input");¬
 54 >------->------->-------input.addEventListener("keypress", on_keypress_by_query_input_element, false);¬
 55 >------->------->-------input.addEventListener("keyup", on_keyup_by_query_input_element, false);¬
 56 >------->-------}¬
```

## その他
AndroidStudioのエミュレータで日本語入力
https://blog.webarata3.link/android-emulator-japanese

WebView側でスクロール管理して画面いっぱいに表示されたい場合に、Layoutを挟みサイズのオプションもしなければならなかったのが、WebViewを貼り付けてデフォルト設定で適用すればOKになっていた。
(古い設定は使えなくなっていた。)

タイトルバーの消し方が変わっていた。前の方法(androidmanifest.xmlのandroid:theme)は使えなくなっている模様。
http://ntoshi1900.hatenablog.jp/entry/2018/01/21/%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E3%83%90%E3%83%BC%E3%82%92%E6%B6%88%E3%81%99%EF%BC%88Android_Studio_3.0.1%EF%BC%89

###
ソフトウェアキーボード表示時にアクティビティ(WebView)がリサイズで追従しない問題。
タイトルバーを消すときに、不要な`<item name="android:windowFullscreen">true</item>`を指定していたのが原因。
```
        <item name="windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowContentOverlay">@null</item>
```

```
        <activity android:name=".MainActivity"
            android:windowSoftInputMode="adjustResize"
```
`android:windowSoftInputMode="adjustResize"`は不要だった。

https://github.com/delight-im/Android-AdvancedWebView/issues/113

spectron electron UIテストの実用例と書き方memo





spectron公式にはtitleをチェックするなどという気の抜けたリアリティのないユースケースしか書いてないし、リンクで示された先のWebDriverIOには2通りの書き方が書いてあってどちらを選べばよいかわからなかったりする。


リアルに動くelectronのUIテストコードはlina_dictoで使っている。githubに公開している。
https://github.com/MichinariNukazawa/lina_dicto
https://github.com/MichinariNukazawa/lina_dicto/blob/master/lina_dicto/test/spec.js



## mochaについて
https://mochajs.org/ に書いてある。
- done()を用いた非同期なテストと待ち受け
- timeout
あたりにお世話になっている。

retry 使いたいのだがサンプル通りに書いてみて使えているのかよくわからないので使ってない。

## Travis CIでtimeoutテスト
うちの現在のtimeout系テストについて、Travis CI環境で実行すると1.5掛けで遅い。
タイムアウト時間はCI環境の性能を考えて指定しないと、向こう側でエラーになる。

## テストの水増し
実行時間の平均化を目的に、テストを水増ししたい。
内側でループする方法と、テストデータを増やす方法がある。
うちでは現在、併用している。テストデータを増やす方法のほうが平均化の効果があったように思う。(平均とってないからわからないが)

```
 23 it ("Dictionary.get_item_from_keyword timeout", function(done) {¬
 24 >-------// ループによる水増しは、平均化にあまり効果があるように見えない。¬
 25 >-------// データ数を増やしたほうが良さそうに見える。¬
 26 >-------const EO_TO_JA_DICT_DATAS = [¬
 27 >------->-------['voc^o',>------>-------{'isMatch':true}],¬
 28 >------->-------['bonan',>------>-------{'isMatch':true}],¬
 29 >------->-------['bonan matenon',>------{'isMatch':true}],¬
 30 >------->-------['kafo',>------->-------{'isMatch':true}],¬
 31 >------->-------['kafolakto',>-->-------{'isMatch':true}],¬
 32 >------->-------['vitra',>------>-------{'isMatch':true}],¬
 33 >------->-------['zorgo',>------>-------{'isMatch':true}],¬
 34 >------->-------['boc^o',>------>-------{'isMatch':false}],¬
 35 >------->-------['zzz',>>------->-------{'isMatch':false}],¬
 36 >------->-------['xxxxxxxxxx',>->-------{'isMatch':false}],¬
 37 >-------];¬
 38 ¬
 39 >-------const datas = EO_TO_JA_DICT_DATAS;¬
 40 >-------for(let c = 0; c < 10; c++){ // 水増しと平均化¬
 41 >------->-------for(let i = 0; i < datas.length; i++){¬
 42 >------->------->-------const data = datas[i];¬
 43 >------->------->-------let res;¬
 44 >------->------->-------res = Dictionary.get_item_from_keyword(dictionary_handle, data[0]);¬
 45 >------->------->-------if(! data[1].isMatch){¬
 46 >------->------->------->-------assert(null === res);¬
 47 >------->------->-------}else{¬
 48 >------->------->------->-------assert(null !== res);¬
 49 >------->------->-------}¬
 50 >------->------->-------//assert(data[1] === res[2]);¬
 51 >------->-------}¬
 52 >-------}¬
 53 ¬
 54 >-------done();¬
 55 }).timeout(1500);¬
```

## mochaでtimeout時間の指定
公式の記法。
```
it('should take less than 500ms', function(done){
  this.timeout(500);
  setTimeout(done, 300);
});
```

うちで現在使っている記法。
```
it('should take less than 500ms', function(done){
  setTimeout(done, 300);
}).timeout(500);
```


## mochaで実行時間が表示されない

早すぎると実行時間が出力されない。
```
  ✓ Dictionary.init_dictionary timeout (201ms)
  ✓ Linad.initialize timeout (778ms)
  ✓ Dictionary.get_index_from_incremental_keyword timeout
  ✓ Linad.getResponsesFromKeystring timeout (416ms)

  4 passing (1s)
```
だいたい100msくらい。
末尾にsetTimeout()を入れて対処。
```
it ("speedy timeout test", function(done) {
    setTimeout(done, 100);
})
```

```
  ✓ Dictionary.init_dictionary timeout (214ms)
  ✓ Linad.initialize timeout (718ms)
  ✓ Dictionary.get_index_from_incremental_keyword timeout (111ms)
  ✓ Linad.getResponsesFromKeystring timeout (393ms)

  4 passing (2s)
```

エスペラント語関連npmパッケージ一覧・簡易調査(2018)


electronでエスペラント辞書を作るにあたってウマ味のあるnpmライブラリが無いかなと思い調査。
https://www.npmjs.com/search?q=esperanto


## new-hope
https://www.npmjs.com/package/new-hope

### 概要
Esperantoのトークナイザ(単語区切り)。
またサンプルサイトの挙動によると、単語の語根と種類(品詞)を返す模様。

トークナイズ自体は単語区切りに限らない場合もあるようだが、本パッケージの場合はサンプルサイトの挙動を見る限り、単語区切り機能の提供という意味で良い模様。
トークナイズの種類
形態素解析    単語ごとに区切る        自然言語処理に用いる
N-gram        文字数ごとに区切る        検索インデックスの作成に用いる

とはいえ。
エスペラントにおいては、英語のように空白区切りするだけで単語区切りできる。
語根もエスペラント語のルールは単純で単語毎に固有だったり例外は無いため簡単に生成出来る。
(日本語などの場合はデータから辞書を生成しておくなど高度な処理が必要。)
中身はとても単純であることが予想される。

言語 ts
テストフレームワーク jasmine (`spec/`以下)
ビルド webpack
ライセンス MIT

https://webtopi.biz/archives/350
https://qiita.com/mogulla3/items/23aaffbe29c4e600876d
http://groonga.org/ja/docs/reference/tokenizers.html

#### ソースコード

とても単純な`splitter.ts`。
```js
export const split = (text: string) => {
  return text.split(/[" " | . | , | ; | : | ? | ! | ( | )]/)
    .map((w) => w.toLowerCase().trim())
    .filter((w) => w !== "")
};
```
https://github.com/maroun-baydoun/new-hope/blob/master/src/splitter.ts

`tokenizer.ts`も100行に満たない。主語(ex.mi)などいくつかを単語リストとのマッチで判定して、あと末尾の変化を見るだけ。

# Najtingalo
brainf*ckインタプリタ。エスペラントと関係ないのでパス。

# dictionary-eo
https://www.npmjs.com/package/dictionary-eo

Hunspellスペルチェッカ経由でLibre OfficeやFirefox等のMozilla製品で使える、と書いてあるっぽい。
辞書データのソース:
http://www.esperantilo.org/index_en.html
エスペラントと複数国語間の単語の対応表である模様。日本語は含まれていない。

これ自体を何かに組み込んで使うものではなさそう。

テストフレームワーク tape?(きいたことないやつっぽい)
ビルド 独自スクリプト
ライセンス 辞書と辞書のキャシュがGPL-2.0、コード等はMIT

### ソースコード
各言語まとめてのリポジトリになっている。
辞書ファイルがママでGitHubに上がっている。
本体コードというか機能に相当するものは、エスペラント語部分には辞書データ以外に何もない模様。
見るものはない。

## ng-datefns-pipes-all-locales
https://www.npmjs.com/package/ng-datefns-pipes-all-locales

日時文字列関数ライブラリ。
日時のロケール毎の文字列表現を、足し引き比較するなどの機能を提供しており、エスペラントも対象に含まれるということの模様。

あっ、ぱっと興味がわかなかったからスルーしてしまったけれど対応sistemoの数とかツッコミどころだったかもしれないのを読んでおけばよかった(もう面倒だからいいやの気持ち)。

## mimosa-esperanto-es6-modules
mimosaなるWeb系ビルドサービスのコメントかなにかをエスペラント語で使えるようになるらしい。
リンク先の使用ライブラリのGitHubリポジトリが消えていないか?

これ単体で使うものではなさそう。

### ソースコード
2項目程度の設定しかない。
見るものはない。

テスト chai
ビルド なし
ライセンス MIT

## stopwords-eo
https://www.npmjs.com/package/stopwords-eo

ストップワードは自然言語処理(検索)を行うにあたって一般的すぎて意味がないため除外する単語(のリスト)。
https://kotobank.jp/word/%E3%82%B9%E3%83%88%E3%83%83%E3%83%97%E3%83%AF%E3%83%BC%E3%83%89-1703083

機能的にはストップワード文字列の入ったarrayを返すだけ。
本体側(stopwordsパッケージ)によると文字列arrayから実際に取り除くメソッドが生えているオブジェクトを提供しているようなのだが。

ところでこのストップワードのソースって何?
と思ったが、リンク先にソース一覧が一応ある。(見てわからないから追ってないけれど。)
https://github.com/stopwords-iso/stopwords-iso/blob/master/CREDITS.md

### ソースコード
ストップワードの列挙されたテキストファイルがあるのみ。
生成コードは本体側(stopwordsリポジトリ)にあるそうだがまあこれもJSON生成するだけと思われる。
見るものはない。

## write-int
https://www.npmjs.com/package/write-int

int型の数字を言語毎の文字列表現にして返す。

### ソースコード
親側で以下の引数3つを用意して、言語固有のwrite()に渡される。
第一引数、親側で整数を絶対値にしてから、下の桁から格納していくarrayを生成。
第二引数、プラマイを渡す。
第三引数、オプションがあるが、英語とエスペラント語では使っていない。性別で変化する語とかで使うのではないかと思う。
また数値がゼロであれば単にzero()を呼ぶ。

エスペラント対応部分はたぶんこれ。
https://github.com/Costava/write-int/blob/master/src/languages/eo.js

マイナスには対応していない模様。(MINが-1なのは「より上」ということか。)
ちょっと読み解くのに時間がかかった。単純な数字表現とはいえコードに起こすとそれなりに複雑ということか。
上位桁から3桁毎に処理して、その3桁毎は変数triで数えて3(つまり9桁)以上はbase配列に入った桁名で共通処理できる。そこから下(tri==2や1)はルールが異なる。

## gulp-esperanto-rocks
https://www.npmjs.com/package/gulp-esperanto-rocks
https://www.npmjs.com/package/grunt-esperanto
https://www.npmjs.com/package/generator-esperanto
https://www.npmjs.com/package/gobble-esperanto
https://www.npmjs.com/package/gulp-esperanto

またビルドシステムか。よくわからないからパス。

## smart-plurals
https://www.npmjs.com/package/smart-plurals

数字を入れると指定の言語により単数・複数(あればその他)の種類が分類されて返る。

### ソースコード
エスペラントは英語と一緒になっている。
https://github.com/scottrippey/Smart-Plurals/blob/master/src/Smart.Plurals/Smart.Plurals.languages-english.js

## karma-esperanto-preprocessor
anglarのプリプロセサに関係するらしい。よくわからないしどうでも良いのでパス。
https://www.npmjs.com/package/karma-esperanto-preprocessor


## bildo
https://github.com/underr/bildo

ピーマン(等?)とリチャード・ストールマンやゲイツの写真のページが出てくるらしい。
エスペラントで名前を付けたジョークアプリなのでは。

## esperanto-bundle-stream
https://www.npmjs.com/package/esperanto-bundle-stream
https://www.npmjs.com/package/gobble-esperanto-bundle

ビルドツール。パス。

## multi-stopwords
https://www.npmjs.com/package/multi-stopwords

またストップワード。こちらは複数言語を同時指定できるということ?


エスペラント辞書を作るにあたってウマ味のありそうなものが少なくてほとんどパスした気がする。
特にビルド関係。そもそもjsのビルドエコシステムあまり使ってないし、まだ英語で書いていたい。

vscode で使っているnpm packageの一覧(2018/10)

次のプロダクトとしてelectron製アプリケーションを作ろうと考えていて、アプリケーション構成を改善するための下調べの一環。

##
npmパッケージの表面を舐めていく。まずは`  "dependencies": {`
興味のないものとかは適当に説明文の単語を舐めて終わりにしてます。

    "applicationinsights": "1.0.5",
Microsoftによるnodejsアプリケーションのパフォーマンス測定パッケージ
https://www.npmjs.com/package/applicationinsights
https://docs.microsoft.com/ja-jp/azure/application-insights/app-insights-nodejs
    "fast-plist": "0.1.2",
PLISTパーサ(apple製品のzip圧縮テキストフォーマット`.ipa`ファイル)
https://www.npmjs.com/package/fast-plist
    "gc-signals": "^0.0.1",
オブジェクトのGCを監視する。
https://www.npmjs.com/package/gc-signals
    "getmac": "1.4.1",
マシンのMACアドレスを取得する(何に使ってるんだこれ...)
https://www.npmjs.com/package/getmac
    "graceful-fs": "4.1.11",
fsの互換実装。openのちょっとした自動再試行などの機能強化がされているとのこと。
https://www.npmjs.com/package/graceful-fs
    "http-proxy-agent": "^2.1.0",
    "https-proxy-agent": "^2.2.1",
URLに対してプロキシ経由アクセスを提供する。
https://www.npmjs.com/package/http-proxy-agent
    "iconv-lite": "0.4.23",
文字コード変換。ネイティブコードを使わずjsだけで出来ているのがウリとのこと。
https://www.npmjs.com/package/iconv-lite
    "jschardet": "1.6.0",
文字コード種別自動検出。Pythonからの移植らしい。
https://www.npmjs.com/package/jschardet
    "keytar": "4.2.1",
Win/Mac/Linuxのネイティブなパスワード管理システムへのバインディング
https://www.npmjs.com/package/keytar
    "minimist": "1.2.0",
コマンドライン引数パーザ。有名どころ。
https://www.npmjs.com/package/minimist
    "native-is-elevated": "^0.2.1",
管理者権限の有無を返す。
https://www.npmjs.com/package/native-is-elevated
    "native-keymap": "1.2.5",
OS/言語固有のキーマップ(共通キーコードからそれへの変換対応表)を返す。
https://www.npmjs.com/package/native-keymap
    "native-watchdog": "1.0.0",
ウォッチドッグタイマ。
js側がビジーになっていないか監視するのが目的で、C++製の別スレッドによる実装。対象がビジーになっていたら殺す、らしい。
https://www.npmjs.com/package/native-watchdog
    "node-pty": "0.7.8",
疑似端末プロセスをフォークする。ターミナルエミュレータ向け。
https://www.npmjs.com/package/node-pty
    "semver": "^5.5.0",
セマンティックバージョンパーザ。バージョン文字列をいい感じにパースして、前後順を判定する。
https://www.npmjs.com/package/semver
    "spdlog": "0.7.2",
C++製のログ出力ライブラリspdlogのnodeバインディング。
Rotateもついているらしい。
https://www.npmjs.com/package/spdlog
https://github.com/gabime/spdlog
    "sudo-prompt": "8.2.0",
必要に応じて管理者権限のダイアログを出す。
https://www.npmjs.com/package/sudo-prompt
    "v8-inspect-profiler": "^0.0.8",
ChromeDevToolsのプロトコルでV8のパフォーマンス測定を行う。
https://www.npmjs.com/package/v8-inspect-profiler
    "vscode-chokidar": "1.6.4",
    "vscode-debugprotocol": "1.32.0",
    "vscode-nsfw": "1.1.1",
    "vscode-ripgrep": "^1.2.2",
    "vscode-sqlite3": "4.0.2",
    "vscode-textmate": "^4.0.1",
    "vscode-xterm": "3.9.0-beta9",
VSCode固有のあれこれと思われるので、興味はあるがパス。さすがに数が多くて疲れてる。
    "winreg": "^1.2.4",
レジストリコマンドベースのWindowsレジストリ操作。
https://www.npmjs.com/package/winreg
    "yauzl": "^2.9.1",
unzipライブラリ。`yet another unzip library for node. `って書いてある。
https://www.npmjs.com/package/yauzl
    "yazl": "^2.4.3"
zip圧縮ライブラリ。上のやつの仲間。

##
`"devDependencies": {`いってみましょう。
    "7zip": "0.0.6",
7zipを扱う機能をnodeに提供するライブラリ。
https://www.npmjs.com/package/7zip
    "@types/keytar": "^4.0.1",
    "@types/minimist": "^1.2.0",
    "@types/mocha": "2.2.39",
    "@types/node": "^8.9.1",
    "@types/semver": "^5.5.0",
    "@types/sinon": "^1.16.36",
    "@types/webpack": "^4.4.10",
    "@types/winreg": "^1.2.30",
他で指定しているパッケージの別バージョンなので略。
    "asar": "^0.14.0",
electronアプリケーションパッケージ(asarファイル)のライブラリ。
https://www.npmjs.com/package/asar
    "chromium-pickle-js": "^0.2.0",
ChromiumによるPickleバイナリファイル(Python関係ファイルフォーマット)のパック・アンパック。
    "clean-css": "3.4.6",
cssオプティマイザ・ミニマイザ。
https://www.npmjs.com/package/clean-css
    "copy-webpack-plugin": "^4.5.2",
webpack関連。
https://www.npmjs.com/package/copy-webpack-plugin
    "coveralls": "^2.11.11",
coveralls.io とかいうサービスにカバレッジレポートを上げるらしい。
https://www.npmjs.com/package/coveralls
https://coveralls.io/
    "cson-parser": "^1.3.3",
CSON(CoffeeのJSON)のパーザ。
https://www.npmjs.com/package/cson-parser
    "debounce": "^1.0.0",
本ライブラリで生成した関数は、2回以上呼ばれた場合に前の実行から一定時間が経つまで次の実行を遅延する。
    "documentdb": "^1.5.1",
AzureのDBと繋げるって書いてある。
https://www.npmjs.com/package/documentdb
    "electron-mksnapshot": "~2.0.0",
mksnapshotパッケージに必要なバイナリをダウンロードするためだけのパッケージ。
https://www.npmjs.com/package/electron-mksnapshot
    "eslint": "^3.4.0",
eslint。
    "event-stream": "^3.1.7",
ストリーム(パイプ)志向な処理シーケンスを提供する。
https://www.npmjs.com/package/event-stream
    "express": "^4.13.1",
サンプルを見るにシンプルなWebサーバ的なものが作れる様子。
https://www.npmjs.com/package/express
    "glob": "^5.0.13",
ファイルパスパターンマッチ(glob)。.gitignore的なものらしい。
`*`を使ったシンプルなものだけでなく、正規表現っぽいのもいける模様。
https://www.npmjs.com/package/glob
    "gulp": "^3.8.9",
    "gulp-atom-electron": "^1.19.0",
    "gulp-azure-storage": "^0.7.0",
    "gulp-buffer": "0.0.2",
    "gulp-concat": "^2.6.0",
    "gulp-cssnano": "^2.1.0",
    "gulp-eslint": "^3.0.1",
    "gulp-filter": "^3.0.0",
    "gulp-flatmap": "^1.0.0",
    "gulp-json-editor": "^2.2.1",
    "gulp-mocha": "^2.1.3",
    "gulp-plumber": "^1.2.0",
    "gulp-remote-src": "^0.4.0",
    "gulp-rename": "^1.2.0",
    "gulp-replace": "^0.5.4",
    "gulp-shell": "^0.5.2",
    "gulp-tsb": "2.0.5",
    "gulp-tslint": "^8.1.2",
    "gulp-uglify": "^3.0.0",
    "gulp-util": "^3.0.6",
    "gulp-vinyl-zip": "^1.2.2",
なんかいろいろある。gulp大変なのかVSCodeが大きいから仕方ないのか。

    "husky": "^0.13.1",
git hookって書いてある。package.confか.huskyrcに設定が書いてあると言う風に読める。
https://www.npmjs.com/package/husky
    "innosetup-compiler": "^5.5.60",
windows向けinnoインストーラシステムを提供する。
https://www.npmjs.com/package/innosetup-compiler
    "is": "^3.1.0",
テストライブラリ。オブジェクトの型など判定するメソッド集。
https://www.npmjs.com/package/is
    "istanbul": "^0.3.17",
カバレッジテストツール。
https://www.npmjs.com/package/istanbul
    "jsdom-no-contextify": "^3.1.0",
ブラウザ無しで使えるdomを提供するjsdomの仲間という理解で良いか。
https://www.npmjs.com/package/jsdom-no-contextify
    "lazy.js": "^0.4.2",
 Underscore や Lodash的なもの? 並列実行ライブラリということでいいのか。
 https://www.npmjs.com/package/lazy.js
    "merge-options": "^1.0.1",
plain objectをマージする。
https://www.npmjs.com/package/merge-options
    "mime": "^1.4.1",
MIME type(text/plainとか)を判定したり適した拡張子を返したりする模様。
https://www.npmjs.com/package/mime
    "minimatch": "^3.0.4",
globを提供する。
https://www.npmjs.com/package/minimatch
    "mkdirp": "^0.5.0",
`mkdir -p`を提供する。
https://www.npmjs.com/package/mkdirp
    "mocha": "^2.2.5",
    "mocha-junit-reporter": "^1.17.0",
テストフレームワーク。
    "optimist": "0.3.5",
コマンドライン引数パーザ
https://www.npmjs.com/package/optimist
    "p-all": "^1.0.0",
並行数を制限しつつPromiseベースのasyncを実行する?
https://www.npmjs.com/package/p-all
    "pump": "^1.0.1",
パイプ的なものを提供する。
https://www.npmjs.com/package/pump
    "queue": "3.0.6",
Queueに詰め込んだタスクを非同期並列実行する。
https://www.npmjs.com/package/queue
    "rcedit": "^1.1.0",
Windows実行ファイルのrcリソースを編集する。
https://www.npmjs.com/package/rcedit
    "remap-istanbul": "^0.6.4",
イスタンブールの仲間と思うのでパス。
    "rimraf": "^2.2.8",
`rm -rf`を提供する。
https://www.npmjs.com/package/rimraf
    "sinon": "^1.17.2",
test用のスタブとモック
https://www.npmjs.com/package/sinon
    "source-map": "^0.4.4",
Source Maps(ミニファイやビルドしたjs,cssのためのデバッグシンボル)を作る
https://www.npmjs.com/package/source-map
    "ts-loader": "^4.4.2",
    "tslint": "^5.9.1",
    "typescript": "3.1.1",
    "typescript-formatter": "7.1.0",
    "uglify-es": "^3.0.18",
typescript系。当面使用予定なしにつきパス。
    "underscore": "^1.8.2",
関数プログラミングライブラリ
https://www.npmjs.com/package/underscore
    "vinyl": "^0.4.5",
    "vinyl-fs": "^2.4.3",
(`basename`などの)ファイルパスユーティリティ。
https://www.npmjs.com/package/vinyl
    "vsce": "1.48.0",
VSCode Extension Manage
https://www.npmjs.com/package/vsce
    "vscode-nls-dev": "3.2.2",
https://www.npmjs.com/package/vscode-nls-dev
    "webpack": "^4.16.5",
    "webpack-cli": "^3.1.0",
    "webpack-stream": "^5.1.1"
webpack。
  },
```

```js

  "repository": {
    "type": "git",
    "url": "https://github.com/Microsoft/vscode.git"
  },
  "bugs": {
    "url": "https://github.com/Microsoft/vscode/issues"
  },
```
特に書くことは無い。

```js
  "optionalDependencies": {
    "windows-foreground-love": "0.1.0",
    "windows-mutex": "^0.2.0",
    "windows-process-tree": "0.2.3"
  }
}
```
まあWindowsのことはいいか、ということで。パス。



jsのasyncなメソッドをfor,loop内で同期的に呼び出す

async/awaitを使ったjavascriptのコードをnodejsで使用する際、非同期呼び出しとなっている関数をloopの中で動機呼び出しする方法。

この前かなり真剣にPromiseのドキュメントを読んで、これをいずれ使うことになるのか、使いこなすの大変そうだな、と思っていたのですが、js,nodejsは既にその先に行っているそうで、それがasync/awaitとのこと。
で、詳しい説明はいろいろ書いてあったのですが、とりあえず脇に置いておいて、私にはasync/awaitの理解よりも先にやりたい作業があり、幸いWebから拾ってきた関数を使えば、使われているasync/awaitの詳細は気にしなくても良さそう....だったのですが。

例えばこういう関数があり、これはasyncにより非同期呼び出し関数になっています。
```
async function getPage(save_dir, year, month){
    // ブラウザを起動する
    const browser = await pptr.launch({
        //slowMo: 50,                // 実行にwaitを入れる
        args: ['--lang=ja,en-US,en']        // デフォルトでは言語設定が英語なので日本語に変更
    });

    // ページつくる
    const page = await browser.newPage();


    // 〜    ...以下略
}
```
こいつは中身に非同期関数をかなり使っており、単にasyncを外せば同期関数になるというものでもありません。

で、asyncされている関数をこういう呼び出しをすると、並列実行されてしまいます。
```
    for(let year = 2018; 2000 <= year; year--){
        for(let month = 1; month <= 12; month++){
            getPage(save_dir, year, month);
        }
    }

```
私の環境では12coreあるUbuntuマシンがフリーズしかけていました。
Ctrl+Cが効かないため、結局物理電源断。

で、対処法。
```
async function run(){
    for(let year = 2018; 2000 <= year; year--){
        for(let month = 1; month <= 12; month++){
            await getPage(save_dir, year, month);
        }
    }
}

run()

```

対象の呼び出しをawaitすればいい。
awaitするためには、呼び出しをasyncで囲んだ関数内でする必要があるようです。

これ、関数呼び出しの上の階層が必ず非同期になります。
上位階層で最終的に同期呼び出しがしたい場合にどうすればいいのかは不明。今回は必要ないので調べものはこれでおしまいでした。

[クロス本草稿]GTK2アプリケーション開発環境


本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
クロス本は、筆者のGTK/Qt/Electronの経験と知見について扱います。具体的な解説書というより技術エッセイです。
本文章には、校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。




# GTK2アプリケーション開発環境

GTK2アプリケーション開発をするために一番最初にしなければならないのは、開発環境の構築だった。
GTK2アプリケーション開発の前、VisualStudioとEclipseの経験があった。
Windowsで開発環境といえばどちらもインストーラを叩くだけでコンパイラもライブラリも全て揃い、ボタンを押せばビルドと実行がされる環境で開発が始められる。
Linux環境でGTK開発を始めるにあたって、ボタン押下をmakeコマンドに置き換えるところからスタートした。

この頃の成果はWindowsでのGTK2開発環境としてgithubに上げてあるので、本章の副読本として参照のこと。
[MichinariNukazawa/GTK2.0-Dev-Env]( https://github.com/MichinariNukazawa/GTK2.0-Dev-Env )

UbuntuターミナルでのGTKアプリケーションからWindows版開発環境は、実際に作業をする際にも、Linux環境のパスなどを参考にできた。
段階的にレベルが上がり、良いステップアップになったと思う。

当時興味をひいたものとして`pkg-config`コマンドがある。
[Wikipedia pkg-config]( https://ja.wikipedia.org/wiki/Pkg-config )
gccは、デフォルトのパスと環境変数から、ビルドに必要なライブラリとヘッダを探す。
Ubuntu上でGTKアプリケーションをビルドする場合、パスの探索先などを追加するオプションをgccに渡す必要がある(-L,-I,-lオプションのこと)。
pkg-configは、ライブラリ名を指定すると、ビルドに必要なgccオプション文字列を展開して返す。
Ubuntu環境で、設定済みのpkg-configにgtkライブラリを指定すれば、勝手に.pcファイルの読み出しからコマンド引数の展開まで勝手にやってくれる。
デフォルトのパスとPKG_CONFIG_PATH環境変数から、指定されたライブラリ名と同名の`*.pc`ファイルを探す。.pcファイルにgccに渡すオプションの情報が書かれており、ライブラリ配布者が用意してライブラリと共に配布する。
.pcファイルのrequireパラメタで依存関係を別の.pcファイルにネスト可能。試したことはないので参照を循環させたらどうなるのか不明。
筆者は.pcファイル自体を作ったことはないが、多分何かのautotools付属物で生成できるのではないかと思う。
pkg-configは最初から入っているか、build-essensialsかlibgtk2-devで導入されるらしく、Ubuntuターミナルだけで開発していた頃は存在を特に意識せずに使用していた。

後年、GTKライブラリを使おうとした際に付属の.pcファイルがフルパスで書かれていて相対パスに置換する必要があり、この頃の知識が役に立った。

Linux環境でしばらくGTKアプリケーションを書いてみてから、移植というほどではないが、WindowsでのGTKアプリケーションのビルドに挑戦した。
Ubuntuで作ったMakefileやソースとプロジェクトをそのまま使うことを意図し、MinGWと公式配布のビルド済みGTKライブラリを使った。
pkg-config.exeがGTKライブラリに添付されているのを見て、自分が使っていたLinuxコマンドというものの実体を理解した。
Ubuntuでは設定済みであったため理解の曖昧だった環境変数(PATHなど)もWindows環境では自分で設定する必要があった。
結果的に、アプリケーション自体はぱっとした成果物を生み出せなかったが、ターミナル環境への理解が進み、その後の開発に繋がっていった。

Windows環境でコマンドプロンプトを起動するのに使っている`!cons.bat`は「30日でできる自作OS入門」で得た知識を元にしている。
プロジェクトディレクトリの中で直接コマンドプロンプトを起動できるため、コマンドプロンプトで面倒なcdを省ける。
また、バッチ側で環境変数をセットしてコマンドプロンプトを起動できる。
現在でもWindowsで開発するプロジェクトではよく使っている。

Windows環境でテキストエディタにTeraTermを使っていたのも、確か「30日本」の影響だったと思う。こちらは最近VSCodeに乗り換えた。


[クロス本草稿]ロギング


本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
クロス本は、筆者のGTK/Qt/Electronの経験と知見について扱います。具体的な解説書というより技術エッセイです。
本文章には、校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。



# ロギング

本物のデスクトップアプリケーションは、バグレポートに添付して原因を解析可能な、本物のデバッグログ(動作ログ)が出力されるべきである。
デバッグログは、アプリケーションの開発中・ユーザ環境での実運用中の両方で、常に出力され続けなければならない。
本章では、デスクトップアプリケーションからのデバッグログのファイル書き出しについて扱う。
開発中のデバッグ手法は基本的には開発環境の章で扱うが、本章で扱う動作ログも無関係ではない。



GTKにはデバッグログのための機構はない。
QtにはデバッグログのためのqDebug()関数群が用意されている。
Electronではconsole.log()関数群を用いる。
筆者は、Electronでは動作ログのファイル書き出しを行っていないため、それは扱わない。
本章は、GTK,Qt,Electron固有のテクニックと言うより、C,C++での文字列操作テクニックや、筆者の方針が多い内容になっている。

デスクトップアプリケーションでのロギングは下記が参考になる。
[Joel on Software やさしいバグトラッキング]( http://local.joelonsoftware.com/wiki/やさしいバグトラッキング )
[Joel on Software ユーザからクラッシュレポートを自動的に取得する方法]( https://www.amazon.co.jp/Joel-Software-Spolsky/dp/4274066304 )
ただ、筆者は上記に書いてあるようなクラッシュレポートの収集はまだ実施できていない。

マルチプラットフォームを考えると、syslog()が使えない。

筆者は、以下の通りにしている。
- デバッグログはファイルに書き出す
- 標準出力,標準エラー出力からリダイレクトしてテキストファイルに書き出すようにする場合もある
- 基本的に、ログにASCII文字以外は使わない
- 上記を前提に、マルチプラットフォーム(Windows)で日本語出力した場合に起こるかもしれない文字化けは基本的に最初から諦める
- フォーマッティングに難があるため、"+"演算子やstreamによる文字列連結を避ける
- 必要があれば独自実装の簡易なログローテーション(削除や圧縮)を行う
- 起動時にアプリケーションのversion情報をdumpする(手動でインクリメントされるリリース番号だけでなく、gitのコミットハッシュ等をバイナリに織り込んで用いる)

streamについて。
フォーマッティングの文法が長くコードの読み書きが難しいため、極力使用を避けている。

ログローテーションについて。
ログファイル名に起動時刻を使用し、アプリケーション起動時に一週間より古い名前の付いたログファイルを検出して、それらをzipするコマンドを叩く、という実装を行った。
なお、zip圧縮はアーカイブに後からファイルを追加していくことができる。
必要なければzip圧縮でなく単に削除するようにすればよい。
Windowsでは標準コマンドが機能不足であったため、[7-Zip コマンドラインバージョン(7za.exe)]( https://sevenzip.osdn.jp/download.html )をバイナリに添付して用いた。
Linuxでは標準のzipコマンドを呼び出せば用が足りる。
MacOSXでは行っていないが、たぶんLinuxと同じ方法で行ける。

アプリケーションのversion情報について。
過去にはビルド日時も入れていたが、これはビルド毎にバイナリハッシュが変わるだけで意味はないため、最近は行っていない。
gitコミットハッシュを付与する際、未commitな変更がないか確認して存在すればその旨("-develop"など)を付与するようにしている。
[vecterion/version.sh]( https://github.com/MichinariNukazawa/vecterion_vge/blob/master/version.sh )
一部抜粋
```bash
# repository
GIT_HASH=$(git log --pretty=format:'%h' -n 1)
GIT_BRANCH=$(git branch | awk '/^\*/ { printf $2 }')
GIT_DATETIME=$(git log --date=iso --pretty=format:"%ad" -n 1)
## @todo can't check to unstaging new file.
GIT_STATUS_SHORT=$(git diff --stat | tail -1)
```
が、gitで未コミットファイルの十分な判定が未解決の問題となっている。
(未ステージングな新しいファイルが存在する場合にそれを検出する良い方法が見つかっていない。)
また、この方法はQt(QtCreator)では、qmakeファイル(.pro)に組み込むことが難しい。
(QtCreatorから外部のMakefileを呼び出すようにしたら、Makefileを呼び出すだけでヌルビルドに4秒余分にかかるようになってしまった)

## GTK

GTKには、GTKライブラリ側のデバッグオプションがあるが、筆者はこれを使ったことがない。
[Running and debugging GTK+ Applications]( https://developer.gnome.org/gtk3/stable/gtk-running.html )

内容が完全にC言語マクロテクニックになってしまうが、筆者は下記の関数マクロを使用している。
[vecterion/include/pv_error.h]( https://github.com/MichinariNukazawa/vecterion_vge/blob/master/include/pv_error.h )
一部抜粋
```c
#define pv_error(fmt, ...)  \
    fprintf(stderr, "error: %s()[%d]: "fmt"\n", __func__, __LINE__, ## __VA_ARGS__)
```
呼び出すマクロでレベルを指定する。
コード上の関数名と行番号を自動付与する。
vecterionでは、関数名があればファイル名が無くてもログの出力箇所が一意に特定できるため、ファイル名は付けていなかった。
(pc_critical()でexit()を呼んでいないのは、筆者が修行不足であったが故。ここはサパっと死ぬべきだった。)

なお、gtkにはg_strdup_new()というユーティリティ関数がある。
これはprintフォーマットで生成した文字列を必要なメモリを確保して入れて返す。確保するので開放する必要もある。

日本語出力した場合どうなっていたか覚えていないが、多分コマンドプロンプトは文字化けする。

## Qt

QtにはqDebug()関数群がある。
呼び出す関数でレベルを指定する。
環境により出力先を切り分けてくれるようなのだが、常にトラップしてファイル出力していたため、筆者は詳細を知らない。
トラップは[qInstallMessageHandler()]( http://doc.qt.io/qt-5/qtglobal.html#qInstallMessageHandler )で行う。
[QDebug Class]( http://doc.qt.io/qt-5/qdebug.html )
[What's New in Qt 5.6.0: Logging to syslog and journald]( https://www.ics.com/blog/whats-new-qt-560-logging-syslog-and-journald )
ドキュメントに使用例でQRectをそのままストリームに渡しているところを見ると、QObjectを渡すだけで適切にデバッグ出力してくれるのかもしれない。

qDebug()はprintフォーマットでないため、16進数やゼロ埋めを使ったデバッグ出力が欲しい場合に苦労した。
(筆者はc++開発では一般にprintフォーマットが存在しないため苦労する。)
QStringにformatという似た機能があったため、なんとかやっていた。
下記使用例はうろ覚え。
```c++
qDebug(QString::format("%1,%2")
    .arg(hex_value, 16)
    .arg(space_fill_value, 3, 10, QChar(' ')))))
    // hex_valueは、続く第二引数が16進数表記を表す
    // space_fill_valueは、続く第二引数が桁数で第三引数が10進数表記を表す(!)、第四引数が埋めに使う文字
```

str.sprint()というメソッドがあり、筆者はこれも使っていた。
staticメンバではないため、QStringのオブジェクトを用意する必要がある。
下記使用例はうろ覚えだったため[stackoverflow]( https://stackoverflow.com/questions/4784155/how-to-format-a-qstring )より改変引用。
```c++
QString str;
qDebug(str.sprintf("%s %d", "string", 213));
```
2018年現在のQt5.11のドキュメントには無いため廃止された模様。

公式ドキュメントによると、古いversionのQtではqDebug()でprintフォーマットが使えた模様。
[Qt3.3 qDebug()]( http://doc.qt.io/archives/3.3/qapplication.html#qDebug )
2018年現在のQt5.11のドキュメントには無い使用法なので、廃止されたものと思われる。

アプリケーションversion番号のgit hasuについては、よく覚えていない。
確か最終的に.proファイルにコマンド出力をマクロ変数(?)に格納する記法があり、それを用いて「.proのマクロから、c++プリプロセッサマクロ置換」、で実装したような気がする。

Qtと関係ない、純粋なC++言語マクロテクニックの話になってしまうが、MicrosoftのMSDNマガジンで書かれたstd::stringをprintフォーマットに渡せるTempleteテクニックがあり、とても気に入っている。
[最新の C++ で Printf を使用する]( https://msdn.microsoft.com/ja-jp/magazine/dn913181.aspx )

日本語出力がどうなるかは覚えていないが、多分Qt(qDebug())側でハンドリングしているのではないかと思う。

## Electron

console.log()でする。
呼び出すメソッドでレベルを指定する。
デバッグコンソールでログレベルをverboseにしておかないとconsole.debug()が出力に表示されないので注意。

chromium由来のconsole.log()には、いくつかの拡張機能が用意されている。
(どうやら他ブラウザに無い場合があるようで、たぶん標準でない...といっても厳密にはECMAScript仕様にはconsole.log()関数は存在しないのではないかと思うが)
- オブジェクトだけを引数に入れるとコンソールでオブジェクトを展開できる
- 簡単なprintフォーマットが使える

lina_dictoではファイル出力等はしていない。
今後本格的なアプリを書いた場合の課題になっている、未解決問題。
たぶんnpmに選ぶのに困るほど多くのロギングパッケージがあるだろうと考えている。

[クロス本草稿]デプロイ







本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
クロス本は、筆者のGTK/Qt/Electronの経験と知見について扱います。具体的な解説書というより技術エッセイです。
本文章には、校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。


# デプロイ

開発というコストの存在を忘れる開発者はいない。
が、空ウィンドウ表示までに環境構築という見えないハードルがあることはついわすれがちになる。
同じように、開発完了からアプリケーションをリリースするまでにも、デプロイという大きな見えないハードルが存在する。



gtk-rs(GTK/Rust)やPyQt(Qt/Python)を使用していないのは、デプロイ方法の十分な情報が期待できないことが大きい。
特に、実用的なアプリケーションは外部ライブラリを使う可能性が高く、アイコン画像などのその他のリソースを使う場合が多い。
それら必要な依存ファイルをインストーラに積載する方法が必要である。

筆者は、デプロイはバイナリと必要な依存ファイルをzipした配布パッケージを作成して行っている。
インストーラの作成はQtアプリケーションのWindows版でしかしたことがない。
また、MacOSXアプリケーションはGTK,Qtでの開発経験はなく、Electronでしかデプロイ・リリースをしたことがない。

MacOSX版でアプリケーション・アイコンを使う場合は別途アイコンの章を参照。

## GTK

GTKにはQtのような独自のインストーラ構築システムは無い。
GTKによる補助としては、インストーラ(配布パッケージ)には添付する必要のある設定ファイルがあり、その設定ファイルを構築するためのプログラムがGTKにより用意されている。

GTKアプリケーションのデプロイには、Windows版バイナリが公式から配布されていないという大きな問題がある。

GTKライブラリのビルドは、依存ライブラリ自体の数が多く、バージョン含む依存関係が複雑である。
集めるだけでも難しい。
GTKアプリケーションを作りたいだけのライト開発者(?)が挑戦すると挫折する危険性が高い。

GTK2の途中までは公式から配布されていた。
...のだが、2018年現在、確認したら`all-in-one bundle`のページがリンク切れしてる。
ただし直リンクからファイルはダウンロードできる。
GTK2ライブラリ公式配布[gtk+-bundle_2.24.10-20120208_win32.zip]( http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.24/gtk+-bundle_2.24.10-20120208_win32.zip )

筆者は、Windows版バイナリは[@niloufarjp 氏]( https://twitter.com/niloufarjp )の配布している野良ライブラリを使用している。

公式による[Download for Windows]( https://www.gtk.org/download/windows.php )
によると、他にmsysのpacmanにwindowsクロスビルドのバイナリがあるため、パッケージマネージャから取得するのが推奨の模様。
パッケージが展開された先からバイナリをコピーして集める方法でもWindows版が得られると思われる。

氏の野良ライブラリから配布パッケージを作るにあたって、
- 付属の設定ファイルを再構築する必要がある。
- フルパスになっている箇所を相対パスに置換する必要がある。
- ディレクトリ内のライブラリのバイナリから必要なものだけ抜き出す作業は基本的にトライアンドエラー。
と、わりと泥臭い。
`gschemas.compiled`,`loaders.cache`に対して、sedしたり、`bin\glib-compile-schemas.exe`,`gtk-update-icon-cache.exe`などを使ったり(詳細は忘れてしまった)。
アイコンキャッシュ周りだったと思うが、使い方の解説がなかったため、GTKのMLで質問したり、野良ライブラリを配布している[@niloufarjp 氏]( https://twitter.com/niloufarjp )に直接尋ねることになった。

Linux版は基本的にはリポジトリから取得すれば開発には足りる。
Ubuntuの場合、`sudo apt install libgtk-3-dev -y`

Windows:
GTK3アプリケーションのWindows版ビルドのデプロイは、圧縮ファイルにまとめるまではクロスコンパイラ等を用いることでWindowsを使用せずすべてLinux上で完結できる。
詳細は[vecterion_vge/deploy/packaging_win64.sh](  )を参考にするとよい。

Linux:
Linux版は作成しなかったのでやり方はわからない。
(Linux版は.debパッケージを作ろうとしていた記憶があったのだが勘違いだった模様。)

MacOSX:
MacOSX版は作成しなかったのでやり方はわからない。

## Qt

Qtには独自のインストーラ構築システムがある。
[Qt Installer Framework Manual]( http://doc.qt.io/qtinstallerframework/index.html )

筆者の経験では、これには、
- 依存ライブラリを収集するプログラム
- アプリケーションを含むディレクトリをインストーラにパッケージするプログラム
が用意されており、Windowsで使用してインストーラを構築したことがある。

Windows:
やり方は同人誌が出ている。
[理ろぐ インストーラを作ろう!]( https://booth.pm/ja/items/122098 )
日本語で貴重な情報が整理されているので必要になったら迷わず買うべき。
筆者は、この同人誌の前の版を購入した同僚が作ったインストーラ作成システムの、説明書を読みながらコマンドラインを叩いただけなので詳細は把握していない。
ただ当時のシステムは依存ライブラリを収集しきれていないようで、2つくらい手作業でコピーする必要があった。

Linux:
Windows版のQtアプリケーションではインストーラを作ったが、同じアプリケーションの、Linux版の配布パッケージをインストーラでない圧縮ファイルとして作成した。
当時のQt Installer FrameworkのLinux版がどうなっていたのかは記憶にない。筆者はlddを使って依存関係を見ながら、手作業でシステムから依存ライブラリ等を集めた。

MacOSX:
MacOSX版は作成しなかったのでやり方はわからない。

## Electron

Electron向けのパッケージングシステム、npmに4種類以上ある模様。
どれを使うのか悩むレベル。

公式ドキュメントでパッケージングを扱っているページ。
[Application Distribution]( https://electronjs.org/docs/tutorial/application-distribution )
[Application Packaging]( https://electronjs.org/docs/tutorial/application-packaging )

筆者は、
- Windows,MacOSXにelectron-packager
- Linuxにelectron-installer-debian
を使っているのだけれど、ドキュメントに記載のパッケージを見ていると[electron-builder]( https://github.com/electron-userland/electron-builder )良さそう。

Windows:
筆者は試していないが、Wineの存在もあってか、インストーラ作成まで作業をLinux上で完結できるらしい。

Linux:
debなどが作れる。
Linux以外の環境で作ったことはないけれど、Linuxのインストーラパッケージは大抵はただの圧縮フォルダなので、多分作れるのではないかと思う。

MacOSX:
きちんと案内していないパッケージシステムも多いけれど、どのパッケージシステムでも、MacOSX向けのインストーラはMacOSX以外では作成できない模様。
[electron-builder]( https://github.com/electron-userland/electron-builder/issues/3210 )もissueにそのような回答がある。
むしろ、MacOSXアプリケーションがLinux上でビルドできるのは衝撃的だった。
実際はダウンロードしてきたビルド済み実行ファイルからアプリケーション固有のファイルを読んでいるだけと思うが。

[クロス本草稿]未調査の項目


# 未調査の項目

本章は、クロスプラットホーム・デスクトップアプリケーションについて、わたしが使ったことのないテクノロジーについて書いている。
なので、他の章よりさらに現実乖離度が高いと思われる。
個人的な印象であることに留意しつつ、短く留めるよう努力する。


## その他のクロスプラットホームFW

クロスプラットホーム・アプリケーションFWはGTK/Qt/Electronだけではない。
ぱっと思いつくものとして、Java,C#,Unity,SPAがある。(これらがFWであるかはともかく)

### Java
JavaはGUIなデスクトップアプリケーションだけのものではないが。
joel氏が「宇宙人が見様見真似で作ったため味を付け忘れたUI、後に修正されたが」と語っていた奴。
<<joelonsoftware本へのリンク>>
Javaの機能は、クロスプラットホームGUIだけでない。

Javaの悪評について。
Javaが使われている環境ではプログラマの扱いが悪いという話が目につくし、Java自体も酷評されている印象がある。
特に恨みもないので擁護するようなことを書いておくことにする。
COBOLを置き換えて長期利用する安定したシステムにJavaが採用されるのは、エコシステムを含めたJava全体で見ればそのようなメリットがあるということだと思う。
Javaが持つ機能は、GC・VM・中間表現その他、当時から見たらどれも革新的な新テクノロジだったと思う。
Javaはそれらの新テクノロジを一度に商用システムに持ち込み一般化したすごいパイオニアだった。
そしてJavaは、商用化についてパイオニアになった。
同時に、パイオニアが犯すことを避けられないあらゆる失敗を抱え込んだのではないかと思う。
悪いことに、Javaは成功した本物の商用言語製品だった。後方互換性を捨てることはできないことは失敗を修正する機会がなかったことも意味する。
長生きすればするほど、Javaは10年前のよく知られた言語設計の失敗を今もそのまま抱えた言語として、悪評も固めていったのではないかと思う。

と、個人的にJava開発環境を悪く思ってはいないが、前述の通りプログラマには劣悪環境であるという話しか聞かない。
個人開発で採用する理由はないし、Java案件に関わらないで済ますためにも、Javaは使いたくない。

### C#
フォームデザイナとかあってすごく使いやすいJavaのようなものらしい。
Linuxでやるのが面倒くさそうなので採用したことはない。

### Unity
ゲーム用。
Qtと同じくモバイルアプリケーションを守備範囲に含む。
ユリアーモの文字割り当て変換(※)のWebサービスを作っていた時、競合がWebサービスをUnityでラップして、デスクトップアプリケーション版をリリースした。
[自家のWebサービス sisto_converter]( https://github.com/MichinariNukazawa/sisto_converter )
[競合のWebサービス EsperantoToJuliamoWithHTML5]( https://sugurunatsuno.github.io/EsperantoToJuliamoWithHTML5/ )
[競合のアプリケーション EsperantoToJuliamo]( https://twitter.com/fuyunosuguru/status/903929591122636800 )
モバイルやゲーム以外にも、Electronと同じ使い方で同じ範囲をカバーできるらしい。
Unityは言語として、C#とJavaScriptが使える。他にもあった気がする。

個人的には、Qtと同じくモバイルアプリケーションに傾注するであろうという予想とか、プロプライエタリであり無償版は起動時ロゴが出る、3D性能が必要なことはしない等の理由で、マルチプラットフォーム・デスクトップアプリケーションFW選択の際に真っ先に脱落する。


ユリアーモは「ことのはアムリラート」劇中に登場する架空の自然言語。
独自のアルファベットを用いる特徴があり、公式からフォントがリリースされている。
[『ことのはアムリラート』ユリアーモフォント配布 公式URL]( http://sukerasparo.com/sp004/font.html )
このフォントのアルファベットの割り当てが特殊であるため、一般に使われるラテン・アルファベットの割り当てを、公式の独自割り当てに変換するサービスが必要となる。

### Webアプリケーション(SPA等)
デスクトップアプリケーションから主役の座を奪い、現在はモバイルアプリケーションと覇権を争っている奴。
滅茶苦茶雑に言えば、ブラウザがあれば動くので、マルチプラットフォーム性が最強。
現実にはいろいろ面倒なところや弱点があり、決定打とはいえない。(このへん列挙は面倒だし、読者を含め私より詳しい人はいくらでもいるので書かない)

ぱっと作れてぱっと使う、家の外やモバイルで使う、軽いツールを作る場合はWebアプリとして実装することが多い。
前述の、ユリアーモの文字割り当て変換は軽いツールの例。

## 使用していないFWの機能

GTKは機能ヘルパ兼依存ライブラリの数が多く、Qtは分厚く範囲の広いクロスプラットホームレイヤであるためこちらも機能が多い。
Electronはわりと機能の少ない小さなアプリケーションを書いたつもりなので、最低限以上の機能は使わないようにしていた。
そのため、筆者が使用していない機能のほうがずっと多い。
そもそも網羅的な入門書を書くのではなく、本物のアプリケーションを書くためにGTK,Qt,Electronを使っているので、全機能を触っておく必要はない...。
(なので本書はこういう感じです。)

というわけで、使っていない機能の話はしておくべきかと思うので本章を書いた。

### Electron:自動アップデート・クラッシュレポート
githubのリリースページにインストーラを置いておくと勝手に認識して自動アップデートしてくれる、とかだと嬉しかったのだが、そういうわけでもない模様。
そもそもElectronを使って作ったlina_dictoはプラットフォーム毎にインストーラを作るのが面倒でzipでリリースしている。
現在はインストーラ形式での配布はしていないので、自動アップデートの前にインストーラ作成しなければ。

クラッシュレポートはJoel先生のブログ記事を読んでからぜひ実装したい機能で、Electronに搭載されていると聞いたときは狂喜乱舞したのだが、いかんせん作ったアプリケーションのユーザベースが少なすぎてコストに見合う気がしない...。

### Qt:QML
JSでアプリケーションを書くなら普通にブラウザアプリのほうがいいと思う...。

### Qt:Android/iOSのモバイルクロス開発
Androidアプリのリリース経験があるが、[lina_dicto for Android]( https://github.com/MichinariNukazawa/lina_dicto_for_android )はElectronで書いたlina_dictoをWebViewで移植して作ったので、QtのAndroid,iOSクロス開発機能は使用しなかった。
QtのAPIがカバーする範囲はよくわからないが、QtのAPIに無い機能については、プラットフォーム毎にネイティブコードを書くし、そういう書き方ができる、とのこと。
<取り消し線>Qtでモバイル開発していた周囲の人々は、iOS11対応で悲鳴を上げていた。</取り消し線>

### Qt:組み込み
死に体のデスクトップアプリケーションからQtが見つけ出した脱出先。
セットトップボックス等でHTMLが重いような場合を想定しているとのこと。
ターゲットによってはQt自体のクロスビルドから自前でする必要がある。
組み込みQtのサポートは販売ライセンスがデスクトップアプリケーション版と異なるという話が。

### GTK:マークアップ言語によるウィンドウ構造記述
QtはQtCreatorをフォームデザイナとしてポトペタするのが一番簡単であるためそれを使うことになるし、目diffを目patchする羽目にでも陥らない限りはQtのXMLを直接触る必要はない。
ElectronはHTMLを書くしかないので、マークアップを使う使わないという話にそもそもならない。
GTKの場合、QtCreator相当のフォームデザイナがクラッシュするような品質であり、マークアップを直接書くのはドキュメントが整備されていたとしても御免こうむるので、コードで直接UIを構築していた。
今はもう少しマシになっているといいなと思う。Windows版はバイナリ配布が無いかもしれないけれど。

### GTK/Qt:非ネイティブな言語バインディング
バインディング境界でバグが発生したら解決できなさそうだったので、生産性を犠牲にして、GTK,Qtではネイティブ言語なC言語,C++言語をそのまま使う選択をした。
また、バインディングはどうしてもネイティブ言語よりドキュメントの優先度が低く、ユーザが少ないこともあって、情報を集めることが難しい。
既にバインディングを使ってアプリケーションを書いてしまった場合や、公式が本当に一級市民扱いのサポートをしている場合以外には、おすすめしない。


[クロス本草稿]ドキュメント


本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
クロス本は、筆者のGtk/Qt/electronの経験と知見について扱います。具体的な解説書というより技術エッセイです。
本文章には、校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。



# ドキュメント

公式ドキュメントのメンテナンスレベルは重要である。

最低限、APIマニュアルとクイックスタートマニュアルとサンプルコードが公式で欲しい。
サンプルコードは初心者状態で触るので、最低限の作業で完動するものであって欲しい。

公式ドキュメントとは別に、Stack OverflowのFAQ・有象無象のブログ記事(Qiita含む)・物理書籍等の、非公式な外部ドキュメントの充実度は気になるところである。
外部ドキュメントの充実度は、人気のバロメータでありフレームワークの将来性を測る指標として採用検討の際に気になるところである。
筆者は公式ドキュメントの他に、ブログ記事があればそちらも読む。

Gtk/Qt/Electronすべてにおいて、残念ながら物理書籍は母数が少なく、タイミングに恵まれないこともあり、筆者は今のところ縁がない。
書籍の内容は大抵、網羅性は高いがAPI解説が主で公式ドキュメントの日本語訳のような者が多く、実用に関するEffectiveな内容はまずない。

公式ドキュメントにおいて、「XXXについては書いてない」「XXXができない」と書いてあることはまずないので、そのへんは筆者の体感を書いた。
そういう時の筆者は、悪魔の証明になるが、APIマニュアルのページタイトルあたりを網羅して書いてないことを確認しつつ、Web上で同じ結論に到達した人が居なかったか確認している。

一応再度注意しておくと、以下に指摘している不備は、当時のもので現在は解決しているかもしれないし、そもそも十分探していないから見つかっていないだけかもしれない。
実際、開発と無関係に本書のために調べてみると、見たことのない新しい資料がわりと出てくる...。
(こんな同人誌出して大丈夫なのか筆者...本当に)

## Gtk

[GTK+ 3 Reference Manual]( https://developer.gnome.org/gtk3/ )
Gtk2とGtk3でドキュメントが分かれている。Gtk3はさらにversion毎にページが分けられている。
<<ここにスクリーンショット>> [GtkAlignment Widget]( https://developer.gnome.org/gtk3/3.22/GtkAlignment.html )


Gtk3バージョン内でのAPI変更(追加・削除ほか)はドキュメント中に記載され、必要に応じてURLリンクなどが追加される。

とても不思議なことに、何故か、Gtkのドキュメントを引くためだけのアプリケーションが存在する。
内容はWebから参照できるものと同じである。
<<ここにスクリーンショット>> [GTK-Doc, Devhelp]( https://wiki.gnome.org/Apps/Devhelp )

Devhelpという名前は名前空間汚染的(ex.Surface,Go言語)だと思う。

CSSについて十分なドキュメントは存在しない。(これについての文句はCSSの章で書くはず。)

配布パッケージの作り方については公式ドキュメントがない。
Gtk3自体がライブラリビルドだけでも依存関係の解決はユーザ側でできるという前提だが、実際にはライブラリ間のversion依存が複雑。
Gtk3ライブラリのビルドだけで挫折or満足してしまう可能性が高い...。
[参考:Gtk2-3野良ビルドをしている @niloufarjp 氏のtwitter]( https://twitter.com/niloufarjp )
以前からライブラリ以外に必要なものはあったが、Gtk3からはアイコンキャッシュ等、さらに複雑になった模様。
またGtk3以降もバージョンにより必要なものが微妙に異なるようで、野良の情報を集めてなんとかする必要がある。

### サンプルコード

アプリケーションサンプル集が、gtk-3-examplesというパッケージ名で、集約されたアプリケーションの形式で存在する。
Widgetの使用方法がメイン。
<<ここにスクリーンショット>>

問題点として、サンプルコードを単にコピペしただけでは動作するアプリケーションのコードにならない。
サンプルコードはmain部分が共通化されており、このmain部分が記載されていないため、探してきて補完しないと手元でサンプルコードをビルドしてみることができない。
(手元のGtkバージョンで同サンプルコードが有効か否か確認する等したい派の筆者は困る。)
また、サンプルコードはanjutaが生成するほど本格的でなくエラーチェックを真面目にしているわけでもないわりに、わかりやすく単純に書かれているわけでもなく、微妙に参考にしにくい。

### 外部ドキュメント

Qt,Electronと比べると、GtkについてはWeb上の情報は少ない。

非公式のドキュメントとして、
[GTK+/GNOMEによるGUIアプリケーションプログラミング (菅谷保之著)]( http://wiki.cis.iwate-u.ac.jp/~suzuki/Soft/GUI-Application-Programming-20070430.pdf )
がある。
なぜかWeb上で無償公開されている。
Gtk3になってAPIが多少変更されたが、これが扱っている範囲はGtk2,Gtk3の間であまり違いはないので十分参照できる。
筆者は一時期、USBメモリに開発環境と一緒に入れて持ち歩いていた。
今見ると、
[入門GTK+ (菅谷保之著)]( http://wiki.cis.iwate-u.ac.jp/~wiki/csd/docs/GTK-book-all.pdf )
というものがやはりWeb上に有り、こちらのほうが新しい?(といっても2012年だとのことだが)
CSS化前のドキュメントなのでCSS関連の内容はない。
Web上の断片情報が少ない一方で、これらのドキュメントに、主要なWidgetおよび基本的な機能が網羅されている。

物理書籍は、Gtk2時代の古い書籍しか存在しない。
物理書籍も販売されている「入門GTK+ (菅谷保之著)」を同人誌と言うのは無理がある。
同人誌でGtkを扱っているものを見た覚えがない。だから本書を書いたところもある。

### その他

救いとして、GtkにはMLがあり、疑問点は質問すれば大抵は数日中に回答がある。
[modal dialogに関するML上での質問]( https://mail.gnome.org/archives/gtk-app-devel-list/2017-June/msg00050.html )
さっき自分の質問タイトルでWeb検索したら引っかからなかったので、検索性に難があるのでは?

## Qt

[Qt Documentation]( http://doc.qt.io/qt-5/ )
Qtのversion毎にドキュメントが分かれているが番号が飛び飛びなので、どうやら長期サポート・メジャー変更あたりで区切っている模様。

QtもCSSの詳細なドキュメントは無いが、QtCreatorのフォームデザイナとCSS入力支援があるので多少マシである。

ARMボードへのクロスビルドに関する公式ガイドページとか無いの? (仕事でわりと面倒な目に遭った)

### サンプルコード

Qtのサンプルコード集はWebサイトに存在する。
QtCreatorからもサンプルにアクセスできる。
[List of Qt Examples]( http://doc.qt.io/qt-5/all-examples.html )
サンプル毎のスクリーンショットが用意されていることもあり、GtkのようなWidgetの使用方法というより、用途ごとのアプリケーションサンプルとしての色合いが強いように見える。
これはGUIツールキットとしてのGtkとクロスプラットホームFWとしてのQtの性質を反映しているところもあると思う。
プロジェクトのファイルが一通り揃っており、これはそのままビルドして実行できるプロジェクト一式なので、Gtkのサンプルコードよりも参考にしやすい。

### 外部ドキュメント

Gtkと比較すると、調べたときに扱っているページが多い。

Qtにはインストーラ作成の仕組みが用意されているが、これについての同人誌が出ている。
今見たらAmazonで取扱があった。
「インストーラーで配ろう! (理ろぐ) Kindle版」(URLは長くなってしまうため略)
技術書典などで見る。たぶんいつもの人が出しているのかなという気がする。

Qt4(QtC++)時代のものだが、オライリーから動物本が出ている。
また、Qt5になってからのQtQuick(QML)の物理本が出ている。

### その他

Qtには何故か専用のStack Overflowみたいなサイトがある。普通に検索に引っかかるので意識する必要はない。
筆者は幸い質問が必要になったことがないため、使ったことはない。

## Electron

[Electron Documentation]( https://electronjs.org/docs )

### サンプルコード
そんなものはない。
ドキュメント中にコード断片として存在する。
Gtk,QtよりさらにFWとしての性質が強い(テンプレートに埋め込む的な)性質が強いせい、またGUIサンプルはそのままHTMLページサンプルになってしまうからか。

### 外部ドキュメント

出たばかりなのもあり、わりと新しい情報が多い。
//ただ、良くも悪くもnodejs,npm周りのテクノロジなので、陳腐化も早いかも。

大きなアドバンテージとして、HTML/CSS/JSなのでSPAなどWebページ設計のための膨大な情報がそのままElectronで使える。
(これはよく言われていてその通り。)

物理書籍が出ている。
[Electronではじめるアプリ開発 ~JavaScript/HTML/CSSでデスクトップアプリを作ろう](URLは長くなってしまうので略)
筆者は本書を見つけた時点で、初心者向けのスタートガイドや網羅情報はすでに必要なくなっていたので購入しなかった。
自動アップデートやエラーレポートなどの踏み込んだ内容は書いてなかった。

技術書典などでちらほら見なくもない。

### その他

公式ドキュメントにプラットフォーム毎のBuild Instructionsなんてページがあるけれど、Electronはnpmでバイナリ取得できるのもあって、こういう情報はGtkの方にこそ欲しい...。

[クロス本草稿]開発環境・IDE








本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
クロス本は、筆者のGtk/Qt/electronの経験と知見について扱います。具体的な解説書というより技術エッセイです。
本文章には、校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。

# 開発環境・IDE

現代的なプログラミング言語やフレームワークには、専用の開発環境が用意されていたり、開発を支援するエディタアドオンが利用されていることが多い。
前者はiOSのXcode、AndroidのAndroidStudio、PythonにはJupitorNotebookがある。後者はVSCodeのc++アドオンなど。
GtkにはAnjuta、QtにはQtCreatorというIDEが用意されており、electronには専用IDEのたぐいはない。

筆者は、Qt以外ではGUIアプリケーション以外でもほぼ全てのコーディングにVimを使っている。
またターミナルによるコマンド操作で開発作業を行うことが多い。

## Gtk

GtkのAnjutaは率直に言って使い物にならない。
Ubuntu14.x-16.xの頃、aptパッケージマネージャから導入したAnjutaが、最初のプロジェクト作成ウィザードを実行中にクラッシュしたことがある。
また、公式によるWindowsバイナリは用意されていない模様。

Anjuta自体は、PyGtk言語バインディングだけでなく、Gtkと関係ない開発も行える汎用IDEを目指しているようだ。
(Java、GnomeShellの拡張、等のプロジェクトが作成できる。eclipseのようなものか。)
Gtk向けの開発支援機能として、GUIによるフォームデザイナ(ポトペタ)機能が用意されている。

ドキュメントの章で書いていると思うが、Gtkユーザの中でもAnjuta使用者は少ないため利用するにあたって参考になる情報もあまりない。
フォームデザイナでWidgetをポトペタした後で、Widgetに求めるプロパティを設定するにはどこを編集すれば良いのかわからない。

2018年現在、今ならさすがに解決されているだろうと思ってUbuntu18.04でAnjutaの動作を確認してみたところ、一番最初のウィザードでgtkのライブラリが足りない旨のエラーが出た。
その後のビルドも `**Error**: You must have `libtool' installed.` などと出て失敗したので、色々と依存関係パッケージが足りていないようである。




デバッガも専用のものはないのでgdbを使用する。
メモリ範囲外アクセスのバグ等でクラッシュする場合など。
ビルド時に-gオプションを忘れないこと。

筆者はAnjutaは使わずに、開発はMakefileを書いてターミナルで実行し、コードはvimで書いている。
Windows向けはUbuntu上でmingwを用いてクロスビルドし、公式がGtk3ライブラリのWindows向けバイナリを提供していないため、非公式なビルドをダウンロードしてGtkアプリケーション開発をしている。
[Windows向けGtk3ライブラリ 非公式配布版]( https://drive.google.com/drive/folders/0BwkefA3JqxdIcGVuNkJfLVFyV2c )
[上記ライブラリを配布している @niloufarjp 氏のtwitter]( https://twitter.com/niloufarjp )
MacOXS版は作ったことがない。

## Qt

GtkのAnjutaが使い物にならないのに対して、QtのQtCreatorは実用的である。

Qt公式によりWindowsおよびLinux版(たしかMacOSXも)のバイナリが提供されている。
QtCreator以外に、VisualStudioでもWindows向けQtアプリケーションが開発可能であるらしい。
ありがちな話として、Ubuntuのパッケージマネージャでインストール可能なQtCreatorは大抵バージョンが古い。

QtCreatorにも汎用IDEを目指す部分はあるようなのだが、基本的には、QtおよびQML、その他のQtの言語バインディングに特化している。
もちろんWidgetのフォームデザイナ機能も用意されている。
GtkよりはWidgetのプロパティ設定も読みやすく設定しやすい。
ある程度ユーザも多いようで、Web上に参考になる情報もある。

QtCreatorのフォームデザイナは、QtCreator向けアドオンで拡張することで、独自定義Widgetを扱えるようになる。
ただし、筆者が以前試した限りでは、このアドオン導入は上手く行かなかった。原因を追求していないので、アドオン側が壊れていたか、単に筆者の手順が悪かっただけかもしれないが。

Ubuntu環境ではQtCreatorを使用してQtC++のプロジェクトをビルドする際に、依存するライブラリの不足で失敗することがあるため、追加パッケージとしてlibglをインストールする必要がある。
(Ubuntuバージョンによってパッケージ名が違うかもしれないので検索でlibgl*-devを探すとよい。)

Ubuntu17付近のIMEがfcitxを使用している環境では、QtCreatorに日本語入力するためにはfcitx-qt5アドオンをソースからビルドしてQtCreatorに導入する必要がある。

QtCreatorをVM上のUbuntuで使用する場合に、OpenGL周りの問題なのかWindowが白塗りとなって内容が表示されない場合がある。
その場合、QtCreatorをターミナルから起動し、起動時オプションでOpenGL周りのオプションを無効にすることで回避できる。

以前はビルドシステムはqmake、自動テストはQTestLibのみだったが、現在はビルドシステムにcmakeや自動テストにgoogletestが選択できるようになった模様。

gdbが組み込まれている。QVectorの範囲外アクセスによる例外などが発生した場合に重宝する。

筆者は公式からWindows向けおよびLinuxのQtCreatorをインストールしてQtアプリケーション開発をしている。
クロスビルドを構築するのは面倒なようなので、配布用バイナリは、Windows/Linuxそれぞれ環境構築してビルドしている。MacOSX版は作ったことがない。
Windows向けはVisualStudio版とMinGW版があり、MinGW版を使用していた。近年、VisualStudio版と統合された模様。

## electron

electronには専用IDEのたぐいは存在しないが、chromiumブラウザ由来の強力な実行時デバッガにより、Gtk/Qtとは段違いに高レベルが高いランタイムな開発支援が受けられる。

electronは専用のフォームデザイナは無い。
electronの画面は普通のHTMLなので、ポトペタがしたければ別途HTMLエディタ的なものを探せばよい。(使ったことがないのでどのようなものがあるかは不明)

だが、chromium由来の機能として、実行中に実行時のウィジェットツリー(DOM構造)と、Widgetプロパティを見ることができる。
DOMツリーに追加したelementが、思った箇所に追加されているか、また思い通りに表示されているかを実際の描画で確認することができる。
Windowサイズ変更した際の、Widgetサイズがどのように変化するかも確認できる。
また、CSSの値も確認することができる。
elementのCSSの値は、親elementからの継承、CSSクラス指定による上書き、style Attributeによる直接指定、jsによる書き換えによって複雑に上書き変化するため、思い通りに表示されない場合、人間が追いかけることは大変むずかしい。
思い通りの表示になっていなければ、動作中にDOMツリーやCSSを書き換えながら、思い通りの表示をする方法を探すことができる。
これらのフォームデザイン補助機能は、圧倒的に便利である。
CSSの章でも書くと思うが、electronの実行時GUIビジュアルデバッガを体験すると、GtkとQtのフォームのデバッガビリティの低さは論外であると理解できるし、特にGtkのCSS採用はあまりに愚かな判断ミスだったとしか思えなくなる。
(※このへんの内容は、もっと詳細にしてCSSの章に移動すると思う)

jsのデバッガが組み込まれている。
gdb,QtCreatorなどと同じく、ブレークポイントや関数呼び出しのトレースなどデバッグに必要な操作が行える。

Gtk,Qtとの違いとして、配布の際もchromiumの全機能が付いたままであるため、デバッガを切り離すことはできない。
ユーザからのデバッガへのアクセスを避けたい場合、デバッガの呼び出しショートカットを無効化すること等で対応する。



筆者はnodejsの最新安定版をnodejs公式からインストールして、そのnpmからelectronパッケージおよび他の依存関係パッケージをインストールしてelectronアプリケーション開発をしている。
開発時のtest等のコマンドはpackage.jsonに書くのが一般的なようなのだが、筆者はパッケージ作成まで含めたMakefileを書いている。
コードはhtml,css,jsまですべてvimで書いている。
開発はLinux上で行い、配布用のWindowsおよびMacOSX向けビルドはLinux上で作成できるためそのようにしている。

[クロス本草稿]テスト-Qt

本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
クロス本は、筆者のGtk/Qt/electronの経験について扱った技術エッセイのようなものです。
校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。


# テスト

本章では各FWにおけるテストの方法について扱う。
GUIアプリケーションの主要なテストには、一般的なユニットテストと、GUIアプリケーション固有のGUIテストのテストの2つがある。
GUIテストにおいては、コールバックイベントをエミュレーションするダミーメッセージの生成と送信、画面更新および描画内容を取得して正解とdiffし結果を確認する方法、という2つの問題がある。

筆者の開発したアプリケーションは、大きなものでも筆者1人が1-2年かけて開発可能な規模のものだった。
そのため、「GUIテストが一覧にされたドキュメント仕様があり、それがすべて自動テスト化されている」というレベルの自動テスト構築は必要ではなかったため、行っていない。
経験は主にユニットテストについてのものであり、GUIテストについては少し書いている。


## Qt

Qtのテストは、なんというか、ほとんどすべてがGtkと逆である。

Qtには、専用のQtTestという名前のテストシステムが用意されている。
QtTestは、ユニットテストとGUIテストの両方をカバーしている。
[Qt Test]( http://doc.qt.io/qt-5/qttest-index.html )
名前空間とインクルードヘッダ名は`QTest`で、qmakeでの追加指定は`QT += testlib`と書く。
また、明記された箇所を見つけることはできなかったが、Qt4までは`QTestLib`と呼ばれ、Qt5以降でQtTestという名前になったのではないかと思う。
[Qt Documentation Archives QTestLib Manual]( http://doc.qt.io/archives/qt-4.8/qtestlib-manual.html )
少々名前が混乱している気がする。Qtの命名ルールに詳しくないので断言はできないが。

テスト専用ではないが、シグナルを監視できる[QSignalSpy Class]( http://doc.qt.io/qt-5/qsignalspy.html )もTestのページに記載されている。
Qtではsignal/slotという名前のメッセージングシステムでGUIを含むイベント処理を行うため、これをQSignalSpyでテストできるという意図なのだろうと想像している。

筆者は結果的に、Qtアプリケーションで自動テストを行わなかった。
そのため経験で語れることはあまりないのだが、ひとつだけ「Qtではgoogletestが使えなかった」というのがある。
当時、Qtアプリでgoogletestが使いたい場合には、Qt公式でない外部のプラグインを導入するなどの工夫が必要があった。
このあたり、Qtがc++風の独自言語により実装されている(※)ため発生した問題かと思っている。
いつからなのかはわからないが、2018年現在、[QtCreaterからgoogletestを選択して使えるようになった模様]( http://doc.qt.io/qtcreator/creator-autotest.html )。
自動テストをしなかったのは、筆者が作ったQtアプリケーションは、QtTestを学ぶかgoogletestを導入するほどの、自動テストの必要性がなかったためである。

※筆者の独自見解。前述の通り(と書いた段階ではまだ「前述」の章を書いていない。書くはず。本当に書くと良いのだが)

[クロス本草稿]テスト-Gtk

本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
クロス本は、筆者のGtk/Qt/electronの経験について扱った技術エッセイのようなものです。
校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。

# テスト

本章では各FWにおけるテストの方法について扱う。
GUIアプリケーションの主要なテストには、一般的なユニットテストと、GUIアプリケーション固有のGUIテストのテストの2つがある。
GUIテストにおいては、コールバックイベントをエミュレーションするダミーメッセージの生成と送信、画面更新および描画内容を取得して正解とdiffし結果を確認する方法、という2つの問題がある。

## Gtk

Gtkには専用のGUIテスト・ユニットテストのテストフレームワークの類はない。
(無いと思う。使っているときには見あたらなかった。無いよね?)


私がGtkアプリケーションを書く際はC言語で書く。そのため、ユニットテストには[googletest]( https://github.com/google/googletest )を採用している。
C言語以外では書いたことがないのでわからないが、Gtkの各言語バインディングでも、言語ごとのデファクトなユニットテストが問題なく使えると思われる。
これは後述の、QtがC++言語を使用していながらgoogletestと衝突することのほうが、どうかしている。

コールバックイベントエミュレーションは、前述の通りフレームワークが無いので自動化していない。
コールバックテスト化のため、一応の工夫が2つある。
- コールバックエミュレーションのユニットテスト化
- 手動テスト(自動化は諦める)
である。








コールバックエミュレーションのユニットテスト化について。
Gtkコールバックループから来たマウスイベントを機能ブロックに送る前に、必要な情報のみをイベント受け渡し用に用意したstructにコピーしてから渡している。
別にGtkネイティブのstructに必要な変数を代入して使っても良いのだが、実際のイベントの取り回し等も考えて、そのような設計にしている。
実際のコード例を挙げると、vecterionの[et_tool_into機能ブロックのテスト]( https://github.com/MichinariNukazawa/vecterion_vge/blob/master/test/utest/test_et_tool_info.cpp )がある。
読んでらえれば、イベントコールバックをなんとかユニットテスト化しようとしている筆者の苦労の様子がわかる。
et_toolはツール、例えば「マウス操作によるアンカーポイント編集」などの集合である。
アプリケーションウィンドウ上でのマウス操作(EtMouseAction)と現在アクティブなツール種別( et_tool_info_util_func_*_mouse_action()関数 )を受け取り、ドキュメント上のアンカーポイントの編集操作や、ポインタの触れた位置へのフォーカス変更などを行う。

手動テスト(自動化は諦める)について。
個人によるOSSでは手動テスト・リリーステストの類のドキュメントをあまり見かけない印象があるのだが、筆者がOSSをあまり多く読んでいないだけかもしれない。
vecterionには手動による[リリーステスト]( https://github.com/MichinariNukazawa/vecterion_vge/blob/master/test/release_test.md )を用意している。
安定版をリリースする前には、リリーステストを実施することにしている。
目的はちょっとしたうっかりバグによる基本機能欠損状態でのリリースの防止。
リリーステストにより実際に、ポカミスのようなバグを何度か発見している。
リリーステスト自体の前文に書いてあるとおり、省力で済むよう心がけているのだが、それでもこんなに項目が多くなってしまうのだった。
正直、手動テスト自体が苦手なのもあり、あまりやりたくはない。

Gtkのテストについて、未調査の事項として、Gtk製の画像編集アプリケーションであるGimpをビルドした際、テストの段階でメインウィンドウと複数のダイアログが一瞬だけ立ち上がって消える。
何らかの実行テストであると思われるのだが、どのような方法で何をテストしているのかは不明。

## Qt

Qtのテストは、なんというか、ほとんどすべてがGtkと逆である。

(続く)

[クロス本草稿] アプリケーション・アイコン

クロス本 注意書き


本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。


# アイコン

アプリケーション・アイコンはデスクトップアプリケーションの顔である。
デスクトップアプリケーション固有のデザインテクニック(主に低解像度対応)があった。
モバイルアプリケーションに技術が引き継がれたかはわからない。
アプリケーション・アイコンはプラットフォームOSにより扱いが異なる。
クロスプラットホーム・アプリケーション・フレームワークは、それを吸収したりしていなかったりする。
というかGtk/Qtはともかく、electronだけはわりと吸収しようとしている。

## アイコンデザイン

アイコンの意匠をデザインする際、サイズ毎に固有のデザインをすることが推奨されている。
ドット絵のテクニックとして有名かもしれない。コーポレートデザインでも用途に合わせて微妙にバリエーション違いを用意するという似た技法がある。
ドット数が大きいアイコンではグラデーションなどを用いて意匠を凝らした美麗イラストを使う。
ドット数の少ないものほど要素を省き影や斜め線の少ないデザインにする。
[Microsoftの Windowsデスクトップ アプリケーション アイコンデザインのガイドライン]( https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa511280.aspx )

MacOSが1024x1024pxの巨大アプリケーションアイコンを使用することが一時期有名だった。
現在では、Windows7-10デスクトップでも大きなアイコンが使用される。
Webページのために16x16pxのfaviconアイコンを用意する時代もあったが、モバイルブラウザでは表示されないため事実上不要となった。
2018年の現代において、小サイズアイコンを専用で用意する意味はほぼ無い。

daisy_bellでは、省力化のために、アイコンはサイズフリーなベクタ画像で1種類のみ作成している。
サイズフリーといってもターゲットサイズが無ければデザインの複雑さレベルの方針が立たないので、WindowsとMacOSXの間を取って、512x512pxサイズを基準にしている。

<<ここにlina_dictoのアイコン>>

lina_dictoでは、辞書アプリケーションでは典型的な開いた本の形をベースに、エスペラント語のテーマカラーから塗りを緑色、エスペラント語のシンボルである緑の星を反転した抜きとして使用した。
緑の星に、たぬきの尻尾が生えているのはもちろんすべての元凶たる「ことのはアムリラート」を意識しているため。
ところで、念の為調べてみたところたぬきの尻尾に縞模様があるとは限らないらしいのだが、まあ深く考えることなく縞模様にした。アライグマになってしまったかもしれない。
リリース時はよく出来たアイコンデザインだと自画自賛していたのだが、後にlina_dicto for englishのアイコンをデザインした際に、lina_dictoのアイコンは複雑過ぎたなと思い直して反省している。
サイズフリーデザインにするための工夫として、緑の星などの意匠は小サイズでは印象から消えて、ぱっと見では「緑色の辞書アイコン」に見えるように、サイズを調整している。

<<ここにlina_dicto for englishのアイコン>>
英語版はあくまでlina_dictoのオマケなので、アイコンデザインも検討の時間すら略する勢いで描いた。
青色は個人的な英語のテーマカラーであるため。端を丸めた線で開いた本の形を示して辞書アプリケーションのアイコンとした。
結果的に、「辞書アプリケーション=本の形」というステレオタイプは強力であったために、本家よりもシンプルさで見れば出来が良いアイコンデザインとなってしまった。
不本意な気持ちを感じなくもない。

<<ここにdaisy sequenceのアイコン>>
業務用を意図しているので黒色単色アイコンで大人っぽく(言い方が子供っぽい)。
シーケンス図のlifelineをテーマにしようと決めて、将来的なPlantUML対応を見据えていたのもあって植物っぽい意匠を取り入れようというところまでは考えたのだけれど、具体的にデザインに落とし込むのが大変だった。
一度は捨てようかと思った。
基本のデザインが決まってからも、重なり部分の抜きとか微妙に調整する必要があり、全体的に難産だった。

## アイコンデータの作成

Windows/MacOSXの場合、複数サイズの画像を1ファイルにまとめたアイコンファイルを用意する必要がある。

Windowsではicoファイルを使用する。
icoファイルはGimpで作成できる。
機械的にサイズ違いのレイヤを作ってicoファイル形式でエクスポートするだけなので、自動生成させたいところだったが、Gimpの自動化は自分にはハードルが高かったため手作業で行っている。
GimpにはPython/Schemeによるスクリプト機能はあるのだが、コマンドラインから叩けるかは不明。

Linuxにはアイコンフォーマットがそもそも存在しないので、画像サイズは1種のみで、普通のPNGとかjpgとかを使う。

MacOSXではiconファイルを使用する。
iconファイルはGimpでは作成できない。プロプライエタリなファイルフォーマットなのか、作成するにはMacOSXが必要である。
フリーな実装として<<実装名>>というコマンドラインツールがあるが、私の環境と用途では正しくアイコンファイルが作成されなかった。
(具体的にどううまく行かなかったのかは覚えていない。)
画像ファイルからiconファイルを作成するWebサービスがある。これは機能する。
[ Convert PNG to ICO and ICNS icons online - iConvert Icons ]( https://iconverticons.com/online/ )
以前一度、数日サービスが止まっていたことがあり、管理者へメールを書いたことがある。後日復活した。
このサービスで作成したアイコンは、Ubuntu18.04付近のNautilusファイルビューアのサムネイルでは砂嵐になり、正しく表示されない。だが正しく機能する。
コマンドラインツールで作成した方は、サムネイルでは正しく表示されていたように思う(が、記憶が定かでない)。

### Windows
Windowsにおいて実行ファイルにリソース情報として作者名などを付与することができる。
実行ファイルのアイコンは、リソースの一種である。
MinGWにWindowsターゲットのリソースエディタがあり、Linux上からでもWindows実行ファイルのリソースを操作できる。
(実行ファイル名: `x86_64-w64-mingw32-windres` あたり。)
それを使って実行ファイルにアイコンをリソースとして連結しているものと思われる。

### Linux
Linux実行形式のELFには、Windowsのようにディストリビューション汎用に使えるアイコンリソースの仕様はない模様。
debやrpmは、アイコンを独立した画像ファイルとして持っている。
インストーラによりインストールする際に、環境によりGnomeやKDEのアプリケーション・ランチャに登録するものと思われる。
インストーラがない場合は、ランチャからはアイコンのないアプリケーションとして扱われる。
(Unityデスクトップではデフォルトのアプリケーションアイコンに、デフォルトのアプリケーションアイコンが使用される。警告のような斜め線の入ったアイコンが使用される)
<<ここにUnityデフォルトのアイコン画像のスクショ>>
Ubuntu18.04でlina_dictoを動かした際に、前のバージョンのUbuntuでは問題なかったアプリケーションアイコンが突然認識されなくなり、デフォルトのアプリケーションアイコンになってしまった。
electronのインストーラ作成パッケージ<<の名前>>のバグに近い何かだろうと思ってissueを立てたところ、後日、アプリケーション側で起動時にアイコン画像を指定することが必要になったことが原因だという返答をもらった。
[electron-installer-debian
/application icon broken in ubuntu 18.04 alpha dash area #135]( https://github.com/unindented/electron-installer-debian/issues/135 )
アイコン画像のフォーマットは普通のPNGを使っている。icoファイルなどが使えるのかは必要なかったため確認していない。

### MacOSX
electronのdeployシステムによるMacOSXバイナリへのアイコン付与が、Linux上でも実行できていることから、MacOSXも実行ファイルのどこかにアイコン画像のデータを埋め込むとそれをOSが読んで表示してくれるものらしい。
(他に書くこととくになさそうなので以上)

## アイコンデータの実行ファイルとの結合

### electron
electronではdeplyシステムがWindows/MacOSXの実行ファイルにアイコンの結合を行う。
[パッケージzip作成 electron-packager]( https://github.com/electron-userland/electron-packager )
前述の通り、Linuxのみdeployとは別に、アプリケーション起動時に画像ファイルの指定が必要となる。
[deb作成 electron-installer-debian]( https://github.com/unindented/electron-installer-debian )

[Linux向けのアプリケーションアイコン登録コード例]( https://github.com/MichinariNukazawa/lina_dicto/blob/master/lina_dicto/index.js )
```js
 13 >-------// Create the browser window.¬
 14 >-------win = new BrowserWindow({¬
 15 >------->-------width: 800,¬
 16 >------->-------height: 600,¬
 17 >------->-------icon: 'image/icon.png'¬
 18 >-------})¬
```

### Gtk
アイコンリソース共通化レイヤのような高度なものはない。
ビルドシステムと同じで、Gtk側で独自なものは用意していないので、WindowsではMinGWに頼るなど、プラットフォーム毎になんとかする。

### Qt
Windowsのrc形式リソースファイルを書いてqmakeビルド設定ファイルに登録しておくことで、ビルド時に勝手に結合される。
Qtはアプリケーションのアイコンを扱うつもりはないらしく、この方法も、どちらかというとWindowsリソース情報のオマケ扱いに思える。
Linux、MacOSXではリソースファイルは単に無視される(とドキュメントに書いてあった気がするし、Linuxでは実際そうだった)。

[qmakeファイルでのRCファイル登録マクロの記述例]( http://d.hatena.ne.jp/QtCoder/20110910/1315838284 )
```
win32:RC_FILE = icon_win32.rc
```
[qmake RC_FILE 参照URL]( http://doc.qt.io/archives/qt-5.5/qmake-variable-reference.html#rc-file )

Linuxコマンドライン上でSVGベクタ画像をJPG等へラスタライズ変換する

 Linuxコマンドライン上でSVGベクタ画像をJPG等へラスタライズ変換することができるが、上手く変換されない場合がある。   以前作った魔法陣イラスト素材をイラスト素材ストックサイトへ登録しようと思い立ち、改めて素材用にラスタライズ変換をかけようとした。   ラスタライズ変換...