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

TestWatcher can no longer access data in ExtensionContext.Store #3944

Closed
joerg1985 opened this issue Sep 2, 2024 · 10 comments
Closed

TestWatcher can no longer access data in ExtensionContext.Store #3944

joerg1985 opened this issue Sep 2, 2024 · 10 comments

Comments

@joerg1985
Copy link

joerg1985 commented Sep 2, 2024

After upgrading from 5.10.2 to 5.11.0 a org.junit.jupiter.api.extension.TestWatcher can no longer access store data. Instead, it now fails with org.junit.jupiter.api.extension.ExtensionContextException: A NamespacedHierarchicalStore cannot be modified or queried after it has been closed.

In my mind the intention of the store is to allow extensions to attach data to the context, so this data should be accessible at least until all extensions related to the context are processed.

Steps to reproduce

import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestWatcher;

class Mock extends TestCase.Handling<TestCase<?, ?>> implements BeforeTestExecutionCallback, TestWatcher {

    static final ExtensionContext.Namespace PACKAGE_PRIVATE = ExtensionContext.Namespace.create(new Object());

    @Override
    public void beforeTestExecution(ExtensionContext context) throws Exception {
        ExtensionContext.Store store = context.getStore(PACKAGE_PRIVATE);

        store.put("value", new Object());
    }

    @Override
    public void testSuccessful(ExtensionContext context) {
        ExtensionContext.Store store = context.getStore(PACKAGE_PRIVATE);

        store.get("value");
    }

}

Context

  • Used versions (Jupiter/Vintage/Platform): 5.11.0
  • Build Tool/IDE: maven 3
@sbrannen sbrannen changed the title TestWatcher can not access store data TestWatcher can no longer access data in ExtensionContext.Store Sep 2, 2024
@sbrannen
Copy link
Member

sbrannen commented Sep 2, 2024

Hi @joerg1985,

Thanks for raising the issue.

I have confirmed that the following all-in-one test class prints Value: enigma on 5.10.x and throws the aforementioned ExtensionContextException on main (5.11.x).

package example;

import example.TestWatcherTests.DemoTestWatcher;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.junit.jupiter.api.extension.TestWatcher;

@ExtendWith(DemoTestWatcher.class)
class TestWatcherTests {

	@Test
	void test() {
	}

	static class DemoTestWatcher implements BeforeTestExecutionCallback, TestWatcher {

		private static final Namespace NAMESPACE = Namespace.create(DemoTestWatcher.class);

		@Override
		public void beforeTestExecution(ExtensionContext context) throws Exception {
			getStore(context).put("value", "enigma");
		}

		@Override
		public void testSuccessful(ExtensionContext context) {
			System.err.println("Value: " + getStore(context).get("value"));
		}

		private static Store getStore(ExtensionContext context) {
			return context.getStore(NAMESPACE);
		}
	}

}

We will discuss this within the team.

@sbrannen
Copy link
Member

sbrannen commented Sep 2, 2024

As for why this happens, org.junit.platform.engine.support.hierarchical.NodeTestTask.cleanUp() is invoked before org.junit.platform.engine.support.hierarchical.NodeTestTask.reportCompletion().

NodeTestTask.cleanUp() closes the ExtensionContext.Store, and NodeTestTask.reportCompletion() invokes TestMethodTestDescriptor.nodeFinished(...) which invokes TestWatcher callbacks.

@sbrannen
Copy link
Member

sbrannen commented Sep 2, 2024

One option to fix the regression would be to remove the rejectIfClose() calls in the following "get" actions, and I verified that the DemoTestWatcher works as expected on main with that change.

That would effectively change the semantics of "closed" to "read-only".

However, we'd still need to keep the rejectIfClose() call for the two getOrComputeIfAbsent() variants, since computing a new value would result in a "write" to the closed store.


In summary, I think we should either change the semantics of "closed" to "read-only" or revert #3614.

Though, now that I think about it, the title of #3614 is "NamespacedHierarchicalStore should fail on modifications after close"; however, we actually implemented the checks for "reads" in addition to "writes" (modifications). So, changing the semantics of "closed" to "read-only" would align with the title (and perhaps the original intent) of that issue.

@sbrannen sbrannen added this to the 5.11.1 milestone Sep 2, 2024
@sbrannen
Copy link
Member

sbrannen commented Sep 2, 2024

Though, now that I think about it, the title of #3614 is "NamespacedHierarchicalStore should fail on modifications after close"; however, we actually implemented the checks for "reads" in addition to "writes" (modifications). So, changing the semantics of "closed" to "read-only" would align with the title (and perhaps the original intent) of that issue.

@leonard84, would changing the semantics of "closed" to "read-only" meet your needs (and original intent) as well?

@marcphilipp
Copy link
Member

Team decision: Make it read-only by removing rejectIfClosed() from the get methods and moving it into the function application in getOrComputeIfAbsent.

@sbrannen
Copy link
Member

Since #3614 was introduced in 5.11, I've moved this to the 5.11.1 milestone.

@sbrannen
Copy link
Member

Reopening to backport to the 5.11.x branch.

@sbrannen sbrannen reopened this Sep 13, 2024
sbrannen added a commit that referenced this issue Sep 13, 2024
Changes made in conjunction with #3614 resulted in an exception being
thrown if a NamespacedHierarchicalStore was queried after it had been
closed. Consequently, TestWatcher callbacks were no longer able to
access data in the Store.

To fix that regression, this commit revises NamespacedHierarchicalStore
so that it no longer throws an exception after it has been closed if
the store is queried via one of the get(...) or
getOrComputeIfAbsent(...) methods; however, if a
getOrComputeIfAbsent(...) invocation results in the computation of a
new value, an exception will still be thrown. In other words, when
a NamespacedHierarchicalStore is closed, it now effectively switches to
ready-only mode.

See: #3614
Fixes: #3944
(cherry picked from commit 3e2cc6c)
@sbrannen
Copy link
Member

Backported to 5.11.x in commit bc87b07.

@joerg1985
Copy link
Author

@sbrannen thank you for taking care and fixing this issue!

@sbrannen
Copy link
Member

@sbrannen thank you for taking care and fixing this issue!

You're welcome, and thanks for bringing it to our attention.

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

No branches or pull requests

3 participants