しおメモ

雑多な技術系ブログです。ニッチな内容が多いです。

Swiftのメソッドごとのコンパイル時間を表示するワンライナー

忙しい人向けに、.xcactivitylogからメソッドのコンパイル時間を抽出するワンライナーです。

gunzip -c -S .xcactivitylog hoge.xcactivitylog | perl -pe "s/\r/\n/g" | grep -E "^\d+\.\d+ms" | sort -nr | uniq | head -100

中身はただのgzipですが、\rが厄介なのでperlで処理しています。

% gunzip -c -S .xcactivitylog *.xcactivitylog | perl -pe "s/\r/\n/g" | grep -E "^\d+\.\d+ms" | sort -nr | uniq | head -100

227.21ms        .../Presentation/View/ToastView.swift:64:10 instance method showWithAnimation()
200.22ms        .../Utility/DependencyAssembly.swift:12:22  class method setup()
192.17ms        .../Presentation/ViewModel/Implementation/ColorSelectorViewModel.swift:75:10        instance method makeFlowLayout(with:)
...

もっと忙しい人向け

現在のディレクトリから最新のログを拾ってきます。便利〜♨

gunzip -c -S .xcactivitylog $(ls -lt | grep -E "\.xcactivitylog$" | head -1 | awk '{print $10}') | perl -pe "s/\r/\n/g" | grep -E "^\d+\.\d+ms" | sort -nr | uniq | head -100

NimbleのPredicateの自作方法とサンプル

Nimbleでテストを書いていた際に、共通部分をPredicateとして自作してまとめたいことがあったので、実装の方法と簡単なサンプルを記しておきます。

実装方法

github.com

Quick/NimbleのMatchersの中で、beEmptybeNilを見るとわかりやすいです。

matcherで使うために、Predicateを返すメソッドを定義する必要があります。
PredicatePredicate.simplePredicate.simpleNilableを使うと簡単に、生成することが出来ます。

func hoge<T>() -> Predicate<T> {
    return Predicate.simple("hoge") {
        // 判定処理
    }
}

PredicateStatusmatchesdoesNotMatchfailの3つからなるenumです。

説明
matches 判定条件に合致
doesNotMatch 判定条件に合致しない
fail 入力が上の2つのいずれの状態にもなりえない

.failは特殊ですが、ドキュメントコメントにあるように、nilexpectに入ったときなどに起こります。
expect(nil).to(equal(1))expect(nil).toNot(equal(1))はどちらもマッチ扱いにならずfailとなります。

サンプル

その1: ダウンキャストして比較

func beA<T, U: Equatable>(_ object: U) -> Predicate<T> {
    return Predicate.simple("be a") { actual in
        guard let actual = try actual.evaluate() as? U else { return .fail }

        return PredicateStatus(bool: actual == object)
    }
}

名前はCoreMatchersから拝借しました。

ダウンキャストに失敗した場合は.failとして、成功した場合は比較を行います。
条件式に対しては、PredicateStatus(bool: Bool)を使うことで簡単にPredicateStatusを作ることが出来ます。

その2: enumのassociated valueを比較

enum HogeCases {
    case hoge(count: Int)
    case fuga
}

func beHogeCountIs(_ expected: Int) -> Predicate<HogeCases> {
    return Predicate.simple("be hoge count is") { actual in
        guard let actual = try actual.evaluate() else { return .fail }
        guard case let .hoge(count) = actual else { return .doesNotMatch }

        return PredicateStatus(bool: count == expected)
    }
}

// 利用例
expect(HogeCases.hoge(count: 1)).to(beHogeCountIs(1))

enumのassociated valueを見る時のPredicateです。
.hogeでなかった場合は、toNotにしたときに引っかかってほしいため、doesNotMatchを返します。

実際のケースでもっと複雑な比較になっても、一度Predicateを作っておけば、複数のテストケースで利用でき、見通しが良くなります。

git push前にrebaseをリマインドする

あとからコミットメッセージ書きたいとか、rebase前提でとりあえず細かくcommit切りたいときに、雑にtempとかWIPとか書いて忘れそうになるので、hookspre-pushに警告をいれてみることにしました。

やり方

master pushを防ぐのとほぼ同じです。

.git/hooks/pre-pushに書いておくことで、それっぽいコミットメッセージに反応して、pushを中断します。

while read local_ref local_sha1 remote_ref remote_sha1
do
  MESSAGES=`git log --oneline origin/${remote_ref##refs/heads/}..HEAD | awk '{print $2}'`
  if [[ $MESSAGES =~ (WIP|wip|temp|tmp) ]]; then
    echo "💪rebaseしろよな!"
    exit 1
  fi
done

とりあえず、自分を律するスタイルでいってみます。