【日記】Kontributorになった

最終的にやったことをまとめた記事を投稿しました。

wrongwrong163377.hatenablog.com


やったこと

JetBrains/kotlinGitHubPRを出し、マージしてもらいました。
当該PRは以下です。 github.com

このPRの内容

このPRに関しては全てテストです(変更行数は2,500行超、結構巨大です)。

このPRは、足りていなかったテストコードの補完兼後続の修正を分かりやすくするためのfailing testであり、本題となる修正は別途PRを発行しています。
内容は、kotlin-reflectnullablevalue classを引数に含む関数を呼び出した際にエラーになるバグの緩和です。 github.com

感想

大まかな感想は以下の3つです。

  • 深掘りを続けてきたKotlinに貢献できたことが嬉しい
  • Kotlinのような巨大プロジェクトでテストパターンが全く足りていない部分が有るとは思わなかった
  • 上手く行けばkotlin-reflectに依存するライブラリ全体のvalue classサポートを進められそうなのでやりがいが有る

深掘りを続けてきたKotlinに貢献できたことが嬉しい

Kotlinに触れたのは、2018年のエウレカでのインターンシップがきっかけでした。 wrongwrong163377.hatenablog.com

そこから利用を続け、バックエンドはJavaだった職場にKotlinを持ち込んだり、Kotlin製の自作OSSを公開したり、moshijackson-module-kotlinのような有名リポジトリにコントリビュートしたり、そしてついにKotlinそのものにコントリビュートすることができました。
ずっと好んで取り組んできたものにコントリビュートできたというのは非常に嬉しかったです。

(どうでもいいことですが、今思い返すと、初めて外部リポジトリにコントリビュートしたのが丁度1年位前でした。) wrongwrong163377.hatenablog.com

Kotlinのような巨大プロジェクトでテストパターンが全く足りていない部分が有るとは思わなかった

前述の通り、テストだけで2,000行超という非常に大きなPRを発行した訳ですが、その原因はそもそも当該箇所に関してテストパターンが全く足りていない状況だったことです。

value classは既にstableとされている機能ですが、実際の所関連するkotlin-reflectの機能には多くの重大なバグが残されています。
今回自分はテストパターンの追加を行いましたが、テストパターンは恐らくまだまだ不足しています。

見たのがkotlin-reflect単体とは言え、Kotlinという大きなプロジェクトにこのような状況が有るというのは自分にとって大きな驚きでした。

上手く行けばkotlin-reflectに依存するライブラリ全体のvalue classサポートを進められそうなのでやりがいが有る

自分は現在KT-31141(本題となるPRで対応している問題)と、KT-27598の緩和に取り組んでいます。
これらの問題は長らくkotlin-reflectに依存するライブラリ全体のvalue classサポートを阻んできたものです。

つまり、自分の修正によって、kotlin-reflectに依存するライブラリ全体のvalue classサポートが進むということになります(あくまで上手く行けばですが)。
少なくともjackson-module-kotlinについては自分の方で進めようと思っています。

自分が書いたコードによってそのような大きな変化が起きるというのは、大規模OSSに貢献する大きなやりがいですね。
これからもぼちぼちやっていこうと思います。

このブログの運用と何をどこにアウトプットするかについて

プログラミング系の内容をQiitaに書きはじめ、趣味向けのサブブログを立ち上げ、その割に何をどこに書くかの話をまとめていませんでした。
ということで、今更ですが、このブログの運用と何をどこにアウトプットするかについてまとめます。

サブブログに何を書くか

wrongwrong-pc-parts.hatenablog.com

サブブログの方には、プログラミング以外の趣味の話や、ガジェット等のレビューを書きます。

Qiitaに何を書くか

qiita.com

Qiitaには、ある程度ボリュームが有って、環境や個別の事情に依存しないような、プログラミング・開発関連の内容を書こうと思っています。
ただし、明確に区切っている訳ではないため、このブログとどちらにアウトプットするかは不定です。

このブログに何を書くか

wrongwrong163377.hatenablog.com

このブログには、ボリュームが無かったり、環境や個別の事情に依存するような内容を中心に書いていきます。
ただし、前述の通り、Qiitaとの使い分けは明確にしていません。

また、たまに日記・意見表明のような記事も書くかもしれません。

終わりに

2017年に始めてから雑多な内容を書き続けてきたこのブログですが、個人的には先ごとにテーマを絞れている今の形の方がいいのかなと思っています。
アウトプット先は分散しましたが、アウトプットそのものは続けていこうと思っているので、引き続きよろしくお願いします。

【Windows】特定フォルダへの書き込み(ファイル作成)を禁止する

やること

あるディレクトリへのファイル作成を禁止します。

注意点

自分はプログラムからの書き込みを禁止しようと考えてこれを実行しましたが、これをやるとプログラムが正常に動かなかったです。

やり方

操作したいフォルダを右クリック -> プロパティを開き、その中のセキュリティタブに移動します。
すると、画像のようにユーザー一覧とアクセス許可が表示されます(画像ではユーザー名を消しているので見え方が不自然になっています)。

f:id:wrongwrongwrongwrong163377:20220130020102j:plain

この画面から編集(E)...を選択すると以下のような画面が開きます。
ここで下側のアクセス許可チェックボックスから、書き込み拒否チェックボックスをチェック・適用するとファイル作成を禁止できます。

f:id:wrongwrongwrongwrong163377:20220130020740j:plain

この状態でファイルの新規作成を選択しても、画像のように何も作成できない状態になっています。

f:id:wrongwrongwrongwrong163377:20220130021149p:plain

【PowerShell】git tagを全て取得する

  • PowerShellgit tagすると、タグの量が多ければエンターを連打しないと続きが出てこない
  • ファイルに出力すれば全て取れる
    • コマンドはgit tag > ${対象ファイル名}

練度の低さがバレてしまう

【Java】Unable to make private static int java.time.OffsetDateTime.compareInstant(java.time.OffsetDateTime,java.time.OffsetDateTime)...への対処

TL;DR

  • Java 16AssertJassertThatを使ってOffsetDateTime同士を比較するとエラーになる場合がある
    • Mockkに関しては直接の原因じゃない?
  • 原因はJDK内部のカプセル化の強化
  • Java 11に変えるか、オプションを指定することで解決できる

本文

冒頭に書いたような状況が発生しました。
その時のスタックトレース(掲載可能な部分)は以下のようになりました。

Unable to make private static int java.time.OffsetDateTime.compareInstant(java.time.OffsetDateTime,java.time.OffsetDateTime) accessible: module java.base does not "opens java.time" to unnamed module @6515c7f6
java.lang.reflect.InaccessibleObjectException: Unable to make private static int java.time.OffsetDateTime.compareInstant(java.time.OffsetDateTime,java.time.OffsetDateTime) accessible: module java.base does not "opens java.time" to unnamed module @6515c7f6
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:357)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
    at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
    at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
    at io.mockk.proxy.jvm.advice.MethodCall.call(MethodCall.kt:13)
    at io.mockk.proxy.jvm.advice.SelfCallEliminatorCallable.call(SelfCallEliminatorCallable.kt:14)
    at io.mockk.impl.instantiation.JvmMockFactoryHelper.handleOriginalCall(JvmMockFactoryHelper.kt:83)
    at io.mockk.impl.instantiation.JvmMockFactoryHelper.access$handleOriginalCall(JvmMockFactoryHelper.kt:20)
    at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1$invocation$$inlined$stdFunctions$lambda$1.invoke(JvmMockFactoryHelper.kt:28)
    at io.mockk.impl.stub.MockKStub$handleInvocation$originalPlusToString$1.invoke(MockKStub.kt:227)
    at io.mockk.impl.stub.SpyKStub.defaultAnswer(SpyKStub.kt:15)
    at io.mockk.impl.stub.MockKStub.answer(MockKStub.kt:42)
    at io.mockk.impl.recording.states.AnsweringState.call(AnsweringState.kt:16)
    at io.mockk.impl.recording.CommonCallRecorder.call(CommonCallRecorder.kt:53)
    at io.mockk.impl.stub.MockKStub.handleInvocation(MockKStub.kt:263)
    at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1.invocation(JvmMockFactoryHelper.kt:25)
    at io.mockk.proxy.jvm.advice.Interceptor.call(Interceptor.kt:20)
    at java.base/java.time.OffsetDateTime.compareInstant(OffsetDateTime.java:182)
    at org.assertj.core.internal.OffsetDateTimeByInstantComparator.compare(OffsetDateTimeByInstantComparator.java:36)
    at org.assertj.core.internal.OffsetDateTimeByInstantComparator.compare(OffsetDateTimeByInstantComparator.java:18)
    at org.assertj.core.internal.ComparatorBasedComparisonStrategy.areEqual(ComparatorBasedComparisonStrategy.java:147)
    at org.assertj.core.internal.Comparables.areEqual(Comparables.java:122)
    at org.assertj.core.internal.Comparables.assertEqual(Comparables.java:117)
    at org.assertj.core.api.AbstractOffsetDateTimeAssert.isEqualTo(AbstractOffsetDateTimeAssert.java:380)

対処方法としては、冒頭で書いた通り、実行するJavaJava 11に変えるかオプションを指定することがあります。
オプション指定に関しては、例えばgradleだと以下のようにできます。

tasks.test {
        jvmArgs("--add-opens", "java.base/java.time=ALL-UNNAMED")
}

参考にさせて頂いた内容

github.com

【SpringBoot】java.lang.NoSuchFieldError: Companionで処理が失敗する状況への対処【OkHttp】

TL;DR

  • 自分の場合、Dependency Management PluginによってOkHttpのバージョンが書き換わったことが原因だった
    • com.squareup.okhttp3:okhttp:4.xが期待される所でcom.squareup.okhttp3:okhttp:3.14.9が利用されていた
  • gradledependenciesに利用したいバージョンのOkHttpを追加することで問題が解決した

本文

以下、問題の詳細やその他の情報について書きます。

Dependency Management Plugin周りの話

Dependency Management Plugin周りの詳細な話は別記事としてQiitaに投稿しています。 qiita.com

なぜこの問題が起きるのか

com.squareup.okhttp3:okhttp:4.x3.xの間には互換性が有りません。
特にKotlinの利用の有無は大きな違いです。
直接的なエラーの原因は、Kotlinによって生成されるであろうフィールドにアクセスしようとして失敗していることです。

Spring側の更新がされていない理由

READMEで言及されている通り、com.squareup.okhttp3:okhttp:3.xは既に更新が終了している状態です。 github.com

にも関わらずSpring側のバージョン指定が更新されていない理由は以下の通りです(issue内のやり取りから要約)。

  • SpringFrameworkcom.squareup.okhttp3:okhttp:3.xに依存している
  • SpringFrameworkKotlinに限ったプロジェクトではないため、Kotlinにも依存するcom.squareup.okhttp3:okhttp:4.xに移行することはできない

github.com

【JUnit5】MethodSourceで空Streamをエラーにしたくない場合の対処

状況

JUnit5ParameterizedTestで、MethodSourceが空Streamを返した場合、以下のようなエラーになります。

Configuration error: You must configure at least one set of arguments for this @ParameterizedTest
org.junit.platform.commons.PreconditionViolationException: Configuration error: You must configure at least one set of arguments for this @ParameterizedTest

このエラーは、例えば「Abstract Classにテストを定義し、各実装でMethodSourceを実装する」ような形でテストを実装している場合問題になります。

対処法

この問題は、EnabledIf/DisabledIfアノテーションを用い、テストを実行しないパターンを設定することで回避できます。
例えば以下のようになります(サンプルコードはKotlinですがJavaでも殆ど同じになります)。

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
abstract class Foo {
    abstract fun provider(): Stream<Arguments>

    private fun providerReturnsEmpty(): Boolean = provider().count() == 0L

    @ParameterizedTest
    @MethodSource("successProvider")
    @DisabledIf(value = "providerReturnsEmpty", disabledReason = "providerが空を返すため")
    fun test(/* 引数略 */) { /* テスト実装略 */ }
}

補足

EnabledIf/DisabledIfはどれ位何ができるか

EnabledIf/DisabledIfの詳しい使い方については以下のドキュメントで紹介されています。

junit.org

また、当該のドキュメントには出てきませんが、Springと組み合わせている場合SpELという記法で書くこともできます。

www.baeldung.com

ただ、個人的には、サンプルで紹介したようにbooleanを返す引数無し関数を定義するやり方をおすすめします。
理由は以下の通りです。

  • 読み書きに知識が必要になって面倒
  • アノテーション内で複雑なことをやり始めると(特にJavaでは)読みにくくなる

ParameterizedTestの方にオプションは無いのか

ParameterizedTestの方で空Streamをエラーにしないオプションは無いのか」と思われるかもしれませんが、それは提供されていないようでした。
詳しくは下記のissueにてやり取りが有ります(このオプションが欲しい方は👍を残すなりお願いします、自分は残しました)。

github.com