-
Notifications
You must be signed in to change notification settings - Fork 23
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
Contextualise CF tck #197
Contextualise CF tck #197
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried out the TCK test on OpenLiberty's implementation, and there are a couple of problems, although after I made adjustments to the TCK locally, everything passed.
First, ManagedExecutorTest and ThreadContextTest are getting the following error:
java.lang.NoClassDefFoundError: org.eclipse.microprofile.context.tck.TckThreadFactory
This is because the test case uses the new TckThreadFactory class, but the class isn't part of the test application. It is easily fixed by adding,
in ManagedExecutorTest:
return ShrinkWrap.create(WebArchive.class, ManagedExecutorTest.class.getSimpleName() + ".war")
.addClass(ManagedExecutorTest.class)
.addClass(org.eclipse.microprofile.context.tck.TckThread.class)
.addClass(org.eclipse.microprofile.context.tck.TckThreadFactory.class)
.addAsLibraries(fakeContextProviders);
and in ThreadContextTest:
return ShrinkWrap.create(WebArchive.class, ThreadContextTest.class.getSimpleName() + ".war")
.addClass(ThreadContextTest.class)
.addClass(ThreadContextTest.class)
.addClass(org.eclipse.microprofile.context.tck.TckThread.class)
.addClass(org.eclipse.microprofile.context.tck.TckThreadFactory.class)
.addAsLibraries(threadPriorityContextProvider, multiContextProvider);
After fixing the previous error, I see 3 occurrences of the following, which is due to the TCK requiring the optional ContextManagerProvider.getContextManagerBuilder method,
java.lang.UnsupportedOperationException
at org.eclipse.microprofile.context.spi.ContextManagerProvider.getContextManagerBuilder(ContextManagerProvider.java:155)
at org.eclipse.microprofile.context.tck.ManagedExecutorTest.withDefaultExecutorServiceIsUsedDirectlyAndViaGetThreadContext(ManagedExecutorTest.java
I made a note of this in each place. The UnsupportedOperationException just needs to be caught and the test skipped because the method is optional.
.getContextManagerBuilder() | ||
.withDefaultExecutorService(executorService) | ||
.addDiscoveredThreadContextProviders() | ||
.build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ContextManagerProvider.getContextManagerBuilder() is optional. We need to allow for implementations that don't support it. I believe this can be done as follows:
ContextManager.Builder contextManagerBuilder;
try {
contextManagerBuilder = ContextManagerProvider.instance().getContextManagerBuilder();
} catch (UnsupportedOperationException x) {
// getContextManagerBuilder is optional. Skip if unimplemented.
return;
}
ContextManager contextManager = contextManagerBuilder
.addDiscoveredThreadContextProviders()
.withDefaultExecutorService(null)
.build();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ouch, I didn't realise that. But then how can I test the executor service in this case? I need to make sure that it works if it's specified and if it's set to null
. If I use the default context manager I can't know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If an implementation doesn't support getContextManagerBuilder, that means there is no way to configure the default executor service. The implementation will either have a built-in default that it uses for async dependent stages of withContextCapture, or it won't have a built-in default, which will mean UnsupportedOperationException is appropriate when attempting to create async stages. In order to test this, we can trap for UnsupportedOperationException on thenApplyAsync(fn), which is considered a passing result when getContextManagerBuilder is rejected, and in the absence of the exception, check that async dependent stages that are created from the copied stage run successfully, independent of any constraints that might be in place on the executor of the stage from which it was originally copied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify, your test is fine, except that it needs to trap UnsupportedOperationException and skip itself in that case. And then a separate test can be written to cover the default context manager, in which the test will need to trap UnsupportedOperationException on thenApplyAsync (or whatever *Async method is used)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, fine, is this better then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly, but two other issues were introduced in the changes. I added comments for those inline instead of under this conversation.
ExecutorService executorService = Executors.newSingleThreadExecutor(new TckThreadFactory()); | ||
ContextManager contextManager = ContextManagerProvider | ||
.instance() | ||
.getContextManagerBuilder() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as previous comment - getContextManagerBuilder is optional, so test needs to skip if not implemented.
throws ExecutionException, InterruptedException, TimeoutException { | ||
ContextManager contextManager = ContextManagerProvider | ||
.instance() | ||
.getContextManagerBuilder() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as previous comment - getContextManagerBuilder is optional, so test needs to skip if not implemented.
af04c29
to
a28ecd0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See inline comments for remaining 2 issues that were introduced in the fix. Also one minor comment that I forgot to mention yesterday - the copyright year should include 2020 on files.
public void withDefaultExecutorServiceIsUsedDirectlyAndViaGetThreadContext() | ||
throws ExecutionException, InterruptedException, TimeoutException { | ||
ExecutorService executorService = Executors.newSingleThreadExecutor(new TckThreadFactory()); | ||
ContextManager contextManager = ThreadContextTest.getContextManagerBuilderIfSupported() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fails with
java.lang.NoClassDefFoundError: org.eclipse.microprofile.context.tck.ThreadContextTest
To fix, just need to add ThreadContextTest.class under addClasses
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooops, done.
.build(); | ||
} catch (SkipException x) { | ||
// not supported, use the default and assume that one does not have a default executor service | ||
contextManager = ContextManagerProvider.instance().getContextManager(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a valid assumption. OpenLiberty's default/built-in context manager does have a default executor service. There is nothing in the spec that would prohibit this and force the assumption to hold, and so the test cannot require it. The Assert.fail
lines that follow should be replaced with checking that the async stage actually does complete successfully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not following. I thought the behaviour of ThreadContext.withContextCapture(CompletionStage)
used to be that *Async
methods should throw, and should keep on throwing if we can't specify a default executor.
Are you saying that OpenLiberty will make them not throw now?
If that is the case, then I can't check at all whether the Async
methods throw or not with OpenLiberty because I can't predict whether or not there will be a default executor and so both options would be fine depending on implementation, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not following. I thought the behaviour of
ThreadContext.withContextCapture(CompletionStage)
used to be that*Async
methods should throw
You are correct about what the requirement used to be.
and should keep on throwing if we can't specify a default executor.
The JavaDoc isn't worded that way. It states that apart from the ManagedExecutor.getThreadContext
path, the new completion stages will use "the default executor service if provided by the platform".
Are you saying that OpenLiberty will make them not throw now?
Yes. OpenLiberty does provide a default executor service and therefore uses it here. The default executor service happens to not be switchable, but that is beside the point. While it would be possible to change the spec to prohibit this, I cannot see why we would change it to prohibit because allowing customers to use the *Async methods is the most useful behavior.
If that is the case, then I can't check at all whether the
Async
methods throw or not with OpenLiberty because I can't predict whether or not there will be a default executor and so both options would be fine depending on implementation, right?
Correct. Both options are valid for implementations, so a test needs to consider both to be passing results. It's still worth keeping the test because it will verify there are no failures at other points.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, thanks. Now I get it. I've updated the test.
.getContextManagerBuilder() | ||
.withDefaultExecutorService(executorService) | ||
.addDiscoveredThreadContextProviders() | ||
.build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly, but two other issues were introduced in the changes. I added comments for those inline instead of under this conversation.
a28ecd0
to
cf3db1e
Compare
ManagedExecutor.copy ManagedExecutor.getThreadContext ContextManager.Builder.withDefaultExecutorService
…t run async actions without executor Because it really depends if there is a default executor, and we have tests for both cases elsewhere (with/without)
cf3db1e
to
dcdeb54
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, and the new tests are all passing now.
Shall I merge it then? |
It all looks good to me and I'm in favor of merging. It looks like you also have @manovotn listed as a reviewer. |
We can wait for @manovotn |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, just forgot to green-stamp this one.
Thanks. |
This is the TCK part of #173 whose code PR was merged.
I've added tests for the following things:
ManagedExecutor.copy(CF/CS)
will propagate contextsManagedExecutor.getThreadContext()
will return an instance with the same propagation settings as theManagedExecutor
ManagedExecutor.newIncompleteFuture()
(hoping all other methods behave the same, I didn't think it was worth testing them all, should I?) will respect the newContextManager.Builder.withDefaultExecutorService()
ThreadContext.withContextPropagation(CF/CS)
will respect the newContextManager.Builder.withDefaultExecutorService()
, and banAsync
methods with no executor if not set, and allow them if setDid I forget something?