Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try once more to restore binary compatibility in runTest #3742

Merged
merged 1 commit into from
May 12, 2023

Conversation

dkhalanskyjb
Copy link
Collaborator

Fixes #3673

@dkhalanskyjb dkhalanskyjb changed the base branch from master to develop May 8, 2023 10:21
@dkhalanskyjb dkhalanskyjb marked this pull request as ready for review May 8, 2023 12:50
@eygraber
Copy link

I "patched" this into my project by defining that function locally, and there are still errors:

java.lang.NoSuchMethodError: 'void kotlinx.coroutines.test.TestBuildersKt.runTest(kotlinx.coroutines.test.TestScope, long, kotlin.jvm.functions.Function2)'

I was able to fix that by adding:

@Deprecated(
  "This is for binary compatibility with the `runTest` overload that existed at some point",
  level = DeprecationLevel.HIDDEN
)
@Suppress("DEPRECATION", "UNUSED_PARAMETER", "UNUSED_VARIABLE")
public fun TestScope.runTest(
  dispatchTimeoutMs: Long,
  testBody: suspend TestScope.() -> Unit
) {}

but I can't call runTest in the body since it causes a StackOverflow.

unused2: Any?,
): TestResult = runTest(dispatchTimeoutMs ?: 60_000, testBody)
): TestResult = runTest(dispatchTimeoutMs, testBody)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
): TestResult = runTest(dispatchTimeoutMs, testBody)
): TestResult = runTest(if (unused1 and 1 != 0) DEFAULT_DISPATCH_TIMEOUT_MS else dispatchTimeoutMs, testBody)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's no need: if somebody did actually provide dispatchTimeoutMs, the $default overload wouldn't be chosen.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that the wrong way around? We're in this function because they didn't supply all the parameters, therefore we need to fill them in.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yes, you're right, it is the other way around. Let me think about it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're fully correct, by decompiling this method in 1.6.4, we get

   // $FF: synthetic method
   public static void runTest$default(TestScope var0, long var1, Function2 var3, int var4, Object var5) {
      if ((var4 & 1) != 0) {
         var1 = 60000L;
      }

      TestBuildersKt.runTest(var0, var1, var3);
   }

Thanks for saving us from one more potential bugfix release!

Copy link
Collaborator

@qwwdfsad qwwdfsad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job.

I haven't dived deep into the restored method's semantics though, it would be nice to manually check compose's library normally operates before the release

@dkhalanskyjb
Copy link
Collaborator Author

On it.

@dkhalanskyjb
Copy link
Collaborator Author

I looked into two reproducers provided to us: #3673 (comment) and #3673 (comment). On both, the issue reproduces on 1.7.0, but with a locally-published version with this patch, one project starts to build successfully, while the other changes the errors from NoSuchMethodError to some business-logic-related ones. I checked, and both do actually call the newly-provided method.

@dkhalanskyjb dkhalanskyjb merged commit c8b3e5e into develop May 12, 2023
@dkhalanskyjb dkhalanskyjb deleted the fix-runTest-binary-compatibility-again branch May 12, 2023 09:34
@eygraber
Copy link

Won't this still be an issue because of #3742 (comment)

@dkhalanskyjb
Copy link
Collaborator Author

@eygraber I don't understand your approach there and don't think it would work anyway, but the likely reason for StackOverflowError is that what you wrote was essentially fun runTest() = runTest(), which, yes, would cause infinite recursion and a stack overflow. Here https://github.com/Kotlin/kotlinx.coroutines/pull/3742/files, you can see that there is no recursion: runTestLegacy (with the JvmName being what it needed to be for binary compatibility) calls runTest, an entirely different function.

@eygraber
Copy link

My point was that it seems like something is also expecting this function to be present:

java.lang.NoSuchMethodError: 'void kotlinx.coroutines.test.TestBuildersKt.runTest(kotlinx.coroutines.test.TestScope, long, kotlin.jvm.functions.Function2)'

Note that it returns void, and has a different signature than the function that was restored in this PR.

dkhalanskyjb added a commit that referenced this pull request May 12, 2023
Follow-up to #3742.
That implementation there did work around issue #3673, but did not restore full binary compatibility: the wrong value of `dispatchTimeoutMs` was passed. Given how `runTest` was used in the problematic library, it should not be a problem, but just to be safe and establish the same behavior even in the deep corner cases, we restore the original implementation fully.
@eygraber
Copy link

Tried with 1.7.1 and it works ¯_(ツ)_/¯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants