Diary

Diary

日々学んだことをアウトプットする場として初めてみました

SwiftPM を使用中にテストコードで missing required module 'xxx'

Swift Package Manager(SwiftPM)を使ってパッケージ管理をしており、firebase 関連のインストールを行なっていました。

そんな中、swiftUI の UITest を記載しようとした時 missing required module 'FirebaseFirestore' のエラーに悩まされたので、その解決策についてメモしておきます。

Binary の Link 先に追加する

  1. Project トップのアイコンをクリックする
  2. TARGETS から UITest ターゲットを選択する
  3. Build Phase > Link Binary With Libraries を確認する
  4. エラーのでたライブラリがなければ追加する

f:id:kokoichi206:20220316204038p:plain

@testable でメインプロジェクトを import する

メインプロジェクトのコードを参照するには @testable をつけることが必要でした

import XCTest
@testable import MainProject

class MainProjectTests: XCTestCase {

}

おわりに

Android Studio に比べて、XCode はスムーズにいかない部分が多い気がします。。。

Bash スクリプトでよく使うテクニックまとめ

コマンド単位の使い方ではなく、作業効率化のためのスクリプトを書く際などに使えるテクニックをまとめてみました。

便利なものがあれば随時追加していきます。

[目次]

エラー対策

使用例

  • ほとんどのケースでつけておいて損がないエラー対策です

使用コマンド

  • set
    • Set Builtin
    • set -e
    • set -u
      • 未定義の変数を参照時にエラー終了する
#!/bin/bash
set -eu

ファイル内容のコメント

個人的に、以下のようにファイルの目的・使用方法をファイルの先頭に書くようにしています。

#!/bin/bash
#
# Description
#    Update CURRENT_PROJECT_VERSION if needed
#
# Usage:
#    bash version_update.sh
#

オプション引数をパースする

使用コマンド

  • 「全ての引数」を表す特殊変数$@をループで処理しています

注意点

  • -n 2などのように『オプション + 値』を取るような場合は注意が必要です
# ======================
# parse arguments (options)
# ======================
for i in "$@"; do
    case $i in
    -h | --help | -help)
        usage_and_exit 0
        ;;
    -s | --slide)
        is_slide=true
        shift 1
        ;;
    -o | --output)
        if [[ -z "$2" ]]; then
            echo "option requires a file name -- $1"
            usage_and_exit 1
        fi
        OUTPUT_PATH="$2"
        shift 2
        ;;
    -*)
        echo "Unknown option $1"
        usage_and_exit 1
        ;;
    *)
        // 何も指定がなければファイル名として解釈したい場合
        if [[ -n "$1" ]] && [[ -f "$1" ]]; then
            FILE="$1"
            shift 1
        fi
        ;;
    esac
done

カスタマイズされたエラーメッセージ

使用例

  • 一部分だけ赤文字にしたエラーメッセージを表示します
  • 標準エラー出力にしています
# ===== print error =====
function print_error() {
    ERROR='\033[1;31m'
    NORMAL='\033[0m'
    echo -e "${ERROR}ERROR${NORMAL}: $1" >&2
}

# 使用例
if [ ! -f "$FILE" ]; then
    print_error "File $FILE doesn't exist"
fi

ファイル終了時に後処理を行う

使用例

  • 途中で処理が失敗したら、事前に作成しておいたバックアップファイルからの復元を行う

使用コマンド

  • trap

注意点

  • エラーが起こりうる行よりも前に trap を呼ぶ
// ファイル終了時に呼び出したい関数を定義する
function recover_from_backup() {
    EXIT_CODE="$?"
    // 終了コードが 0 より大きい場合、エラー終了したことを意味する
    if [[ "$EXIT_CODE" -gt 0 ]]; then
        echo "Something went wrong."
        echo "recovered from backup"
    else
        echo "Exit correctly"
    fi
}
# ERR is special feature to bash
trap recover_from_backup EXIT

helpを表示する

使用例

  • コマンドの使い方及び-hオプションを渡されたときの出力例です
PROGRAM="$(basename "$0")"

# ===== print usage =====
function print_usage() {
    echo "Usage: $PROGRAM [OPTION] FILE"
    echo "  -h, --help, -help"
    echo "      print manual"
    echo "  -o <filename>, --output <filename>"
    echo "      output filename"
}

help を表示して終了:ただし終了ステータスを変えたい

使用例

  • コマンドの使用方法(help)を表示 + 実行終了を行いたいが、終了コードを使い分けたい
    • --helpオプションに対しては 0(正常終了)
    • ファイルが存在しない等のエラーに対しては 1(異常終了)
usage_and_exit()
{
    print_usage
    exit "$1"
}

# オプションの -h に対する使用例
usage_and_exit 0

# エラー時の使用例
usage_and_exit 1

特定のファイルに対して1行ごとに処理を行う

while read -r line
do
    echo "$line"
done < "$FILE"

変数が正規表現に一致してるかチェック

注意点

  • [が2つの方法
  • =~で比較する
if [[ "$line" =~ ^\`\`\`.* ]]; then
    echo "Code block start (or end)"
fi

おわりに

スクリプトを書く際はテンプレに沿うケースが多いと思って、自分がよく使うものをまとめてみました。何か一つでも良いと思っていただけたら嬉しいです。

iphone の画面を mac 上に表示する方法

開発中などに iphone の画面を mac 上に表示させたいことがあるかと思います。

android には scrcpy という優秀なツールがあるのですが、iphone ではどうやら QuickTime Player を使えばできるらしいので、今回はその紹介をしたいと思います

起動方法

0. maciphone をケーブルで繋げる

1. QuickTime Player を起動する

2. File > New Movie Recording を起動する

f:id:kokoichi206:20220313203252p:plain

3. 起動した録画画面で、Camera の設定から iPhone を選択する

f:id:kokoichi206:20220313203414p:plain

以上で、無事 iphonemac 上に描画されます

参考

おわりに

この QuickTime Player を使った方法では PC 上からの操作はできないので、その点は残念です。

最初は xcode からエミュレーターと同じように起動させられろう、と思って探していたのですが設定が見つかりませんでした。ご存じの方がいたら教えていただきたいです。

bash でコマンドの実行結果をファイルとして扱う

プロセス置換(Process Substitution)を用います。使い方と一緒に、ぜひ名前も覚えましょう。

使い方

# 通常のファイル入力の場合
$ cat test.txt

# コマンドの実行結果をファイルとして扱う場合
$ cat <(echo "hoge from echo command")
hoge from echo command

ファイル名<(コマンド)で置き換えるだけです!

<(の間にスペースはいりません。

少し具体的なコマンドで見てみます

diff コマンド

diff <(echo 3.0+3 | bc) <(echo $((3.0+3)) )
1c1
< 6.0
---
> 6.

片方をファイルにして、もう一方をプロセス置換にすることもできます

$ cat file.txt
file

$ diff file.txt <(echo "content")
1c1
< file
---
> content

ファイル読み込み:while read ...

ファイルの中身を読み込みながら1行ずつ処理を行う場合、次のように書くことができます

while read line
do
    echo "$line"
done < file.txt

この while ループに入力させている file.txt の部分に、プロセス置換を使うことが可能です。

### expected outputs
# hoge
# pien
while read line
do
    echo "$line"
done < <(echo -e "hoge\npien")

おわりに

ファイル入力しか受け付けないコマンドに対し、『ちょっとしたテストコードを試したいが、わざわざファイルを用意するのもめんどくさい』といったケースに使えると感じました。

他にも有用なユースケースがあれば教えていただきたいです。

ラズパイサーバーに100万アクセスし温度を計測してみた

自作ラズパイサーバーに高負荷?(40分100万アクセス)をかけ、その際の温度変化を計測してみました。

結果

f:id:kokoichi206:20220210225415p:plain

[目次]

環境

- クライアントサイド
  - zsh 5.8 (mac)
  - go version go1.16.10
- サーバーサイド
  - raspberry pi 4
  - OS: ubuntu 20
  - Apache/2.4.52 (Ubuntu)
  - mydns のドメイン

ラズパイに高負荷をかける

https://mysite.jp の部分には自分のアクセスしたいアドレスを入れてください(決して知らない外部のURLにしないでください

bash で行う

この方法が最速ではないですが、以下の方法では 1000 アクセス 8 秒くらいでした。

time (seq 1000 | xargs -I ZZ -P 5 curl -s "https://mysite.jp" > /dev/null)                                                                                                             
( ... ; )  8.19s user 5.01s system 194% cpu 6.782 total

以下のように background で各アクセス処理を走らせるようにすることでスピードアップできると思います。ただ自分には、バックグラウンド処理をうまく待つことができず、時間が測れませんでした。

time (seq 10000 | xargs -n 1 -P 5 curl -s "https://kokoichi0206.mydns.jp" > /dev/null 2>&1 &)
( ...)  0.00s user 0.00s system 11% cpu 0.009 total

golang を使った方法

こちらのQiitaの記事をそのまま使わせてもらいました。

記事内の Local サーバーを使った方法を試したところ、1 万アクセス 1 秒かからなかったので期待していたのですが、実際の URL に対して行うとかなり遅くなりました。

1000 1_0000 100_0000
2.82 28.479 2657.5

ほぼ線形に変化しているようです(サーバー側で同時アクセ数の制限とかある?)。

ということは、常に一定の負荷がかかっていると思って良さそうです。

ラズパイの温度を計測する

ラズパイの温度

以下のコマンドでラズパイの温度を取得できます。

$ cat /sys/class/thermal/thermal_zone0/temp
55991

注意点としては何故か 1000 倍されていることで、上の例は 55.99 度ということになります。

定期的に測定する

以下のような measurement.sh ファイルを用意し、定期的に測定しました(1秒に1測定しています)。

#/bin/sh -eu
#
# Description
#   measure the temperature regularly

date=`date '+%Y%m%d_%H%M%S'`
while :
do
    temp=`cat /sys/class/thermal/thermal_zone0/temp`
    echo `date '+%Y%m%d_%H%M%S'` "$temp" >> "temp_$date"
    sleep 1
done
# Ctrl + C で止める
$ bash measurement.sh

1列目に時間、2列目に温度を記録するようにしています。例としては以下のようなデータが取れます。

20220210_134439 46738
20220210_134440 48686
20220210_134441 48199
20220210_134442 48686

gnuplot でグラフを出力する

なぜ gnuplot を使って描画してるかって?

ワンライナーで使えるからです!!!!

measurement.sh で2列分データを取りましたが、結局1列しか使っていません。

gnuplot -e 'set terminal png; set output "./temperature.png"; set xlabel "time [s]"; set ylabel "temperature [°C]"; plot "temperature" using 0:($2/1000) title "temperature"'

2500 秒くらいで急に落ちてるのは 100 万アクセスが終わった時間になります。

f:id:kokoichi206:20220210225415p:plain

綺麗なカーブを描いていたので log スケールで取ってみたら、綺麗な直線になりました!

gnuplot -e 'set terminal png; set output "./temperature.png"; set xlabel "sec"; set ylabel "temperature"; plot "temperature" using 0:($2/1000) title "temperature"'

f:id:kokoichi206:20220211000320p:plain

最初の方の点が少ないので、より細かく見るために測定間隔を 0.1 秒にして測り直しました。

f:id:kokoichi206:20220211002943p:plain

(おまけ)フィット

両対数を取って線形になる、ということは、以下のような式が成り立つことを意味してます

\displaystyle{
\log(y-y_0) = a*\log(x-x_0) \\
y = c*(x-x_0)^a + y_0

}

以下のコマンドで、x0 を0に固定してxが20から1500までの範囲でフィットしてみます。

gnuplot -e 'f(x)=a*x**b+c; fit[20:1500] f(x) "temperature" using 0:($2/1000) via a,b,c; set terminal png; set output "./temperature_fit.png"; set yrange [45:70]; set xlabel "time [s]"; set ylabel "temperature [°C]"; set key right bottom; plot "temperature" using 0:($2/1000) title "temperature", f(x) lw 5'

結果:(y=a*x**b + c)

a               = 13.1365          +/- 1.717        (13.07%)
b               = 0.121676         +/- 0.009238     (7.593%)
c               = 37.0958          +/- 2.04         (5.498%)

f:id:kokoichi206:20220211005353p:plain

理論わからんけどフィットできたぞおおおおお

おわりに

本当はラズパイサーバーに高負荷をかけてサーバーをダウンさせてみたかったのですが、うまくいかなかった意外にラズパイが丈夫だったので代わりに温度を測ってみました。

http 通信が、ローカルで試した時よりも速度が出なかったのが敗因だと思っています。知らない間に DOS 対策の WAF など(?)働いてるかもしれないので、いつかじっくりと調べてみます!

awk 1 とは何か

awk 1という表現についてメモしておきます。

awk

大前提として、awkの基本パターンは『(マッチ)パターン+アクション』です。

そして awk では以下のように記述します

# awk 'pattern {action}'
$ echo 'hoge fuga pien' | awk '/fuga/ {print $0}'
hoge fuga pien

どちらか一方の省略

実は「パターン」or「アクション」はどちらか一方を省略することができます。

パターンが省略された場合

パターンが省略された場合、全ての行に対してアクションが行われます。

$ echo -e 'hoge fuga pien\npoe ho'
hoge fuga pien
poe ho

$ echo -e 'hoge fuga pien\npoe ho' | awk '{print $1}'
hoge
poe

アクションが省略された場合

アクションが省略された場合、{print $0}と同等のアクションが行われます。

$ echo -e 'hoge fuga pien\npoe ho'
hoge fuga pien
poe ho

# awk '/fuga/ {print $0}' と同等
$ echo -e 'hoge fuga pien\npoe ho' | awk '/fuga/'
hoge fuga pien

awk 1

以上awkの基本的性質を押さえておくと、awk 1という表現の意味が見えてきます。

この表現は、アクションが省略されたものであるり、また、パターンが『1』であることが分かります。

よって、awk 1 とは全ての入力行に対し{print $0}を行うものとなります。

これにより最終行に改行を入れることなどができます。

# 行末に改行がない
xx@yy:~$ echo -en 'hoge'
hogexx@yy:~$

xx@yy:~$ echo -en 'hoge' | awk 1
hoge
xx@yy:~$

なお、awk では 0 より大きな値は真の判定がなされるため、awk 3などでも同様の効果があります。

# 0 より大きな値
xx@yy:~$ echo -en 'hoge' | awk 3
hoge
xx@yy:~$

おわりに

今回は、ぱっと見で何をしているのかわからなかった awk コマンドについて記載してみました。

次やるときに迷わないようしたいです!

Android アプリ内で使用 OSS を表示させる

今回は、必要な OSS の一覧を Google Play services を使って簡単に表示させてみたいと思います。

注意点としては、表示のために新たに Activity を立ち上げる必要があることです。

見た目

f:id:kokoichi206:20220206213339p:plain

環境

- Android Studio: Arctic Fox | 2020.3.1
- gradle: build:gradle:7.0.4
- 使用api: services-oss-licenses:17.0.0

実装

プロジェクトレベルの build.gradle

dependenciesに、次の classpath を追加します。

buildscript {
    ...
    dependencies {
        ...
        classpath 'com.google.android.gms:oss-licenses-plugin:0.10.4'
    }
}

アプリレベルの build.gradle

pluginsdependenciesに必要なものを追加します。

'com.android.application'が追加されていることを確認するようにしてください。

plugins {
    ...
    id 'com.android.application'
    id 'com.google.android.gms.oss-licenses-plugin'
}


android {
    ...
}


dependencies {
    ...
    // OSS License display
    implementation 'com.google.android.gms:play-services-oss-licenses:17.0.0'
}

AndroidManifest に登録

activity を登録しておきます

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    <application
        ...>
        ...

        <activity
            android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
            android:theme="@style/Theme.CountDownTimer" />
        <activity
            android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
            android:theme="@style/Theme.CountDownTimer" />
    </application>
</manifest>

使い方

表示させたいタイミングで、以下のような Activity をスタートさせます。

import com.google.android.gms.oss.licenses.OssLicensesMenuActivity

startActivity(Intent(this, OssLicensesMenuActivity::class.java))

POM dependencies というものに基づきアプリに関与した OSS を表示してくれているため、おそらく大量のライセンスが表示されると思います。

また、タップすることで個別のライセンスを表示できます。

生成物

表示されているライセンス情報一覧は、以下の build フォルダに生成されています。

app/build/generated/third_party_licenses/res/raw/

Links

おわりに

OSS利用規約により、使用 OSS のライセンスを表示させないといけないケースは多いと思います。とはいえ、そこに多くの時間はかけられないので、このようなものを公式が用意してくれているのはありがたいです。