You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by di...@apache.org on 2021/11/12 10:33:54 UTC

[sling-org-apache-sling-i18n] branch master updated: SLING-10912: fix flaky ConcurrentJcrResourceBundleLoadingTest (#8)

This is an automated email from the ASF dual-hosted git repository.

diru pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-i18n.git


The following commit(s) were added to refs/heads/master by this push:
     new c61f0e8  SLING-10912: fix flaky ConcurrentJcrResourceBundleLoadingTest (#8)
c61f0e8 is described below

commit c61f0e8d85da4bf85b42212e88862db28b17bb05
Author: Dirk Rudolph <di...@gmail.com>
AuthorDate: Fri Nov 12 11:32:05 2021 +0100

    SLING-10912: fix flaky ConcurrentJcrResourceBundleLoadingTest (#8)
---
 .../ConcurrentJcrResourceBundleLoadingTest.java    | 65 ++++++++++++----------
 1 file changed, 37 insertions(+), 28 deletions(-)

diff --git a/src/test/java/org/apache/sling/i18n/impl/ConcurrentJcrResourceBundleLoadingTest.java b/src/test/java/org/apache/sling/i18n/impl/ConcurrentJcrResourceBundleLoadingTest.java
index d0d5249..a8784af 100644
--- a/src/test/java/org/apache/sling/i18n/impl/ConcurrentJcrResourceBundleLoadingTest.java
+++ b/src/test/java/org/apache/sling/i18n/impl/ConcurrentJcrResourceBundleLoadingTest.java
@@ -19,7 +19,7 @@
 package org.apache.sling.i18n.impl;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
 import static org.mockito.AdditionalMatchers.or;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -36,10 +36,11 @@ import java.lang.annotation.Annotation;
 import java.util.Arrays;
 import java.util.Locale;
 import java.util.ResourceBundle;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.i18n.impl.JcrResourceBundleProvider.Key;
@@ -50,8 +51,6 @@ import org.junit.runners.Parameterized;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 import org.mockito.verification.VerificationMode;
 import org.osgi.framework.BundleContext;
 import org.powermock.api.mockito.PowerMockito;
@@ -104,7 +103,7 @@ public class ConcurrentJcrResourceBundleLoadingTest {
             public long invalidation_delay() {
                 return 5000;
             }
-            
+
             @Override
             public String[] excluded_paths() {
                 return new String[] {"/var/eventing"};
@@ -194,39 +193,49 @@ public class ConcurrentJcrResourceBundleLoadingTest {
     public void newBundleReplacesOldBundleAfterReload() throws Exception {
         final ResourceBundle oldBundle = provider.getResourceBundle(Locale.ENGLISH);
         final ResourceBundle newBundle = mock(JcrResourceBundle.class);
-        final AtomicBoolean newBundleReady = new AtomicBoolean(false);
+        final CountDownLatch newBundleReturned = new CountDownLatch(1);
 
-        doAnswer(new Answer<ResourceBundle>() {
-            @Override public ResourceBundle answer(InvocationOnMock invocationOnMock) throws Throwable {
-                Thread.sleep(1000);
-                newBundleReady.set(true);
-                return newBundle;
-            }
+        doAnswer(invocationOnMock -> {
+            Thread.sleep(1000);
+            return newBundle;
         }).when(provider, "createResourceBundle", or(ArgumentMatchers.isNull(), any(ResourceResolver.class)), eq(null), eq(Locale.ENGLISH));
 
-        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
-            @Override
-            public void run() {
-                ResourceBundle expected = newBundleReady.get() ? newBundle : oldBundle;
-                assertSame(expected, provider.getResourceBundle(Locale.ENGLISH));
-            }
-        }, 0, 200, TimeUnit.MILLISECONDS);
+        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
+        executorService.scheduleAtFixedRate(
+            () -> {
+                ResourceBundle currentBundle = provider.getResourceBundle(Locale.ENGLISH);
+                if (currentBundle == newBundle) {
+                    // Shutdown the executor once we got the new ResourceBundle. This will cancel the future and opens the gate below.
+                    // Do not assert the returned bundle directly here, as this may become flaky when the bundle returned by the
+                    // mock above was returned but not yet put into the cache.
+                    executorService.shutdownNow();
+                    newBundleReturned.countDown();
+                }
+            },
+            // start with an initial delay to not call getResourceBundle again before we call reloadBundle below
+            200,
+            200,
+            TimeUnit.MILLISECONDS);
 
         provider.reloadBundle(new Key(null, Locale.ENGLISH));
 
+        // wait until the scheduled future gets canceled by shutting down the executor service
+        // this means the new bundle got returned and is in the cache
+        if (!newBundleReturned.await(5000, TimeUnit.MILLISECONDS)) {
+            fail("expected cancellation");
+        }
+        // CancellationException expected
+
         // we expect getResourceBundleInternal() called once in the beginning of the test and once again after reloading the bundle.
         final int expectedGetResourceBundleInternal = 2;
-        VerificationMode verificationMode;
-
-        if (preload) {
+        VerificationMode verificationMode = preload
             // when preloading the calls to getResourceBundleInternal are non-blocking and so more calls will happen while reloading.
             // assuming at least one more
-            verificationMode = atLeast(expectedGetResourceBundleInternal + 1);
-        } else {
-            verificationMode = times(expectedGetResourceBundleInternal);
-        }
+            ? atLeast(expectedGetResourceBundleInternal + 1)
+            : times(expectedGetResourceBundleInternal);
+
+        verifyPrivate(provider, verificationMode).invoke("getResourceBundleInternal",
+            or(ArgumentMatchers.isNull(), any(ResourceResolver.class)), eq(null), eq(Locale.ENGLISH), anyBoolean());
 
-        verifyPrivate(provider, verificationMode)
-                .invoke("getResourceBundleInternal", or(ArgumentMatchers.isNull(), any(ResourceResolver.class)), eq(null), eq(Locale.ENGLISH), anyBoolean());
     }
 }
\ No newline at end of file