You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2019/06/06 06:34:58 UTC

[camel] branch CAMEL-13491 created (now ffa6a29)

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

davsclaus pushed a change to branch CAMEL-13491
in repository https://gitbox.apache.org/repos/asf/camel.git.


      at ffa6a29  CAMEL-13491: Fixed camel-test with isCreateCamelContextPerClass = true to correctly setup/teardown only once before/after class and reset in between test methods. This is a little bit tricky to do with JUnit 4

This branch includes the following new commits:

     new ffa6a29  CAMEL-13491: Fixed camel-test with isCreateCamelContextPerClass = true to correctly setup/teardown only once before/after class and reset in between test methods. This is a little bit tricky to do with JUnit 4

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[camel] 01/01: CAMEL-13491: Fixed camel-test with isCreateCamelContextPerClass = true to correctly setup/teardown only once before/after class and reset in between test methods. This is a little bit tricky to do with JUnit 4

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch CAMEL-13491
in repository https://gitbox.apache.org/repos/asf/camel.git

commit ffa6a29eb392649e6d77cedade94023751ae6ba5
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Jun 6 08:34:40 2019 +0200

    CAMEL-13491: Fixed camel-test with isCreateCamelContextPerClass = true to correctly setup/teardown only once before/after class and reset in between test methods. This is a little bit tricky to do with JUnit 4
---
 .../camel/test/junit4/CamelTearDownRule.java       |  46 +++++++
 .../apache/camel/test/junit4/CamelTestSupport.java |  74 +++++------
 .../org/apache/camel/test/junit4/TestSupport.java  |   5 +-
 .../CreateCamelContextPerTestFalseTest.java        | 116 ++++++++++++++++
 .../CreateCamelContextPerTestTrueTest.java         | 148 +++++++++++++++++++++
 5 files changed, 349 insertions(+), 40 deletions(-)

diff --git a/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTearDownRule.java b/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTearDownRule.java
new file mode 100644
index 0000000..23c89b8
--- /dev/null
+++ b/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTearDownRule.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit4;
+
+import org.junit.rules.ExternalResource;
+
+public class CamelTearDownRule extends ExternalResource {
+
+    private final ThreadLocal<CamelTestSupport> testSupport;
+
+    public CamelTearDownRule(ThreadLocal<CamelTestSupport> testSupport) {
+        this.testSupport = testSupport;
+    }
+
+    @Override
+    protected void before() throws Throwable {
+        super.before();
+    }
+
+    @Override
+    protected void after() {
+        CamelTestSupport support = testSupport.get();
+        if (support != null && support.isCreateCamelContextPerClass()) {
+            try {
+                support.tearDownCreateCamelContextPerClass();
+            } catch (Throwable e) {
+                // ignore
+            }
+        }
+        super.after();
+    }
+}
diff --git a/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java b/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java
index 02c167b..ce1ca20 100644
--- a/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java
+++ b/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java
@@ -88,6 +88,7 @@ import org.apache.camel.util.StopWatch;
 import org.apache.camel.util.TimeUtils;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -120,8 +121,11 @@ public abstract class CamelTestSupport extends TestSupport {
     private final DebugBreakpoint breakpoint = new DebugBreakpoint();
     private final StopWatch watch = new StopWatch();
     private final Map<String, String> fromEndpoints = new HashMap<>();
-    private final AtomicInteger tests = new AtomicInteger(0);
+    private static final ThreadLocal<AtomicInteger> TESTS = new ThreadLocal<>();
+    private static final ThreadLocal<CamelTestSupport> INSTANCE = new ThreadLocal<>();
     private CamelTestWatcher camelTestWatcher = new CamelTestWatcher();
+    @ClassRule
+    public static final CamelTearDownRule CAMEL_TEAR_DOWN_RULE = new CamelTearDownRule(INSTANCE);
 
     /**
      * Use the RouteBuilder or not
@@ -298,24 +302,25 @@ public abstract class CamelTestSupport extends TestSupport {
         log.info("********************************************************************************");
 
         if (isCreateCamelContextPerClass()) {
-            while (true) {
-                int v = tests.get();
-                if (tests.compareAndSet(v, v + 1)) {
-                    if (v == 0) {
-                        // test is per class, so only setup once (the first time)
-                        doSpringBootCheck();
-                        setupResources();
-                        doPreSetup();
-                        doSetUp();
-                        doPostSetup();
-                    } else {
-                        // and in between tests we must do IoC and reset mocks
-                        postProcessTest();
-                        resetMocks();
-                    }
-
-                    break;
-                }
+            INSTANCE.set(this);
+            AtomicInteger v = TESTS.get();
+            if (v == null) {
+                v = new AtomicInteger();
+                TESTS.set(v);
+            }
+            if (v.getAndIncrement() == 0) {
+                LOG.debug("Setup CamelContext before running first test");
+                // test is per class, so only setup once (the first time)
+                doSpringBootCheck();
+                setupResources();
+                doPreSetup();
+                doSetUp();
+                doPostSetup();
+            } else {
+                LOG.debug("Reset between test methods");
+                // and in between tests we must do IoC and reset mocks
+                postProcessTest();
+                resetMocks();
             }
         } else {
             // test is per test so always setup
@@ -510,27 +515,9 @@ public abstract class CamelTestSupport extends TestSupport {
         log.info("********************************************************************************");
 
         if (isCreateCamelContextPerClass()) {
-            while (true) {
-                int v = tests.get();
-                if (v <= 0) {
-                    LOG.warn("Test already teared down");
-                    break;
-                }
-
-                if (tests.compareAndSet(v, v - 1)) {
-                    if (v == 1) {
-                        LOG.debug("tearDown test");
-                        doStopTemplates(threadConsumer.get(), threadTemplate.get(), threadFluentTemplate.get());
-                        doStopCamelContext(threadCamelContext.get(), threadService.get());
-                        doPostTearDown();
-                        cleanupResources();
-                    }
-
-                    break;
-                }
-            }
+            // will tear down test specially in CamelTearDownRule
         } else {
-            LOG.debug("tearDown test");
+            LOG.debug("tearDown()");
             doStopTemplates(consumer, template, fluentTemplate);
             doStopCamelContext(context, camelContextService);
             doPostTearDown();
@@ -538,6 +525,15 @@ public abstract class CamelTestSupport extends TestSupport {
         }
     }
 
+    void tearDownCreateCamelContextPerClass() throws Exception {
+        LOG.debug("tearDownCreateCamelContextPerClass()");
+        TESTS.remove();
+        doStopTemplates(threadConsumer.get(), threadTemplate.get(), threadFluentTemplate.get());
+        doStopCamelContext(threadCamelContext.get(), threadService.get());
+        doPostTearDown();
+        cleanupResources();
+    }
+
     /**
      * Strategy to perform any post action, after {@link CamelContext} is stopped
      */
diff --git a/components/camel-test/src/main/java/org/apache/camel/test/junit4/TestSupport.java b/components/camel-test/src/main/java/org/apache/camel/test/junit4/TestSupport.java
index ddf10e1..3f47d96 100644
--- a/components/camel-test/src/main/java/org/apache/camel/test/junit4/TestSupport.java
+++ b/components/camel-test/src/main/java/org/apache/camel/test/junit4/TestSupport.java
@@ -20,6 +20,7 @@ import java.io.File;
 import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Endpoint;
@@ -38,6 +39,8 @@ import org.apache.camel.support.PredicateAssertHelper;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.rules.TestName;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,8 +52,8 @@ public abstract class TestSupport extends Assert {
     protected static final String LS = System.lineSeparator();
     private static final Logger LOG = LoggerFactory.getLogger(TestSupport.class);
     protected Logger log = LoggerFactory.getLogger(getClass());
-
     private TestName testName = new TestName();
+    private CamelTestWatcher camelTestWatcher = new CamelTestWatcher();
 
     // Builder methods for expressions used when testing
     // -------------------------------------------------------------------------
diff --git a/components/camel-test/src/test/java/org/apache/camel/test/patterns/CreateCamelContextPerTestFalseTest.java b/components/camel-test/src/test/java/org/apache/camel/test/patterns/CreateCamelContextPerTestFalseTest.java
new file mode 100644
index 0000000..059d88b
--- /dev/null
+++ b/components/camel-test/src/test/java/org/apache/camel/test/patterns/CreateCamelContextPerTestFalseTest.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.patterns;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CreateCamelContextPerTestFalseTest extends CamelTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CreateCamelContextPerTestFalseTest.class);
+
+    private static final AtomicInteger CREATED_CONTEXTS = new AtomicInteger();
+    private static final AtomicInteger POST_TEAR_DOWN = new AtomicInteger();
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint resultEndpoint;
+
+    @Produce("direct:start")
+    protected ProducerTemplate template;
+
+    @Override
+    public boolean isCreateCamelContextPerClass() {
+        return false;
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        LOG.info("createCamelContext()");
+        CREATED_CONTEXTS.incrementAndGet();
+        return super.createCamelContext();
+    }
+
+    @Override
+    protected void doPostTearDown() throws Exception {
+        LOG.info("doPostTearDown()");
+        POST_TEAR_DOWN.incrementAndGet();
+        super.doPostTearDown();
+    }
+
+    @Test
+    public void testSendMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertTrue("Should create 1 or more CamelContext per test class", CREATED_CONTEXTS.get() >= 1);
+    }
+
+    @Test
+    public void testSendAnotherMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertTrue("Should create 1 or more CamelContext per test class", CREATED_CONTEXTS.get() >= 1);
+    }
+
+    @Test
+    public void testSendNotMatchingMessage() throws Exception {
+        resultEndpoint.expectedMessageCount(0);
+
+        template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertTrue("Should create 1 or more CamelContext per test class", CREATED_CONTEXTS.get() >= 1);
+    }
+
+    @AfterClass
+    public static void validateTearDown() throws Exception {
+        assertEquals(3, CREATED_CONTEXTS.get());
+        assertEquals(3, POST_TEAR_DOWN.get());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-test/src/test/java/org/apache/camel/test/patterns/CreateCamelContextPerTestTrueTest.java b/components/camel-test/src/test/java/org/apache/camel/test/patterns/CreateCamelContextPerTestTrueTest.java
new file mode 100644
index 0000000..dd1e9db
--- /dev/null
+++ b/components/camel-test/src/test/java/org/apache/camel/test/patterns/CreateCamelContextPerTestTrueTest.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.patterns;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.apache.camel.util.StopWatch;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CreateCamelContextPerTestTrueTest extends CamelTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CreateCamelContextPerTestTrueTest.class);
+
+    private static final AtomicInteger CREATED_CONTEXTS = new AtomicInteger();
+    private static final AtomicInteger POST_TEAR_DOWN = new AtomicInteger();
+    private static final CountDownLatch LATCH = new CountDownLatch(1);
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint resultEndpoint;
+
+    @Produce("direct:start")
+    protected ProducerTemplate template;
+
+    @Override
+    public boolean isCreateCamelContextPerClass() {
+        return true;
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        LOG.info("createCamelContext()");
+        CREATED_CONTEXTS.incrementAndGet();
+        return super.createCamelContext();
+    }
+
+    @Override
+    protected void doPostTearDown() throws Exception {
+        LOG.info("doPostTearDown()");
+        POST_TEAR_DOWN.incrementAndGet();
+        super.doPostTearDown();
+        LATCH.await(5, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void testSendMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertEquals("Should only create 1 CamelContext per test class", 1, CREATED_CONTEXTS.get());
+        assertEquals("Should not call postTearDown yet", 0, POST_TEAR_DOWN.get());
+    }
+
+    @Test
+    public void testSendAnotherMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertEquals("Should only create 1 CamelContext per test class", 1, CREATED_CONTEXTS.get());
+        assertEquals("Should not call postTearDown yet", 0, POST_TEAR_DOWN.get());
+    }
+
+    @Test
+    public void testSendNotMatchingMessage() throws Exception {
+        resultEndpoint.expectedMessageCount(0);
+
+        template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertEquals("Should only create 1 CamelContext per test class", 1, CREATED_CONTEXTS.get());
+        assertEquals("Should not call postTearDown yet", 0, POST_TEAR_DOWN.get());
+    }
+
+    @AfterClass
+    public static void shouldTearDown() throws Exception {
+        // we are called before doPostTearDown so lets wait for that to be called
+        Runnable r = () -> {
+            try {
+                StopWatch watch = new StopWatch();
+                while (watch.taken() < 5000) {
+                    LOG.debug("Checking for tear down called correctly");
+                    if (POST_TEAR_DOWN.get() == 1) {
+                        LATCH.countDown();
+                        break;
+                    } else {
+                        try {
+                            Thread.sleep(100);
+                        } catch (InterruptedException e) {
+                            break;
+                        }
+                    }
+                }
+            } finally {
+                LOG.info("Should only call postTearDown 1 time per test class, called: " + POST_TEAR_DOWN.get());
+                assertEquals("Should only call postTearDown 1 time per test class", 1, POST_TEAR_DOWN.get());
+            }
+        };
+        Thread t = new Thread(r);
+        t.setDaemon(false);
+        t.setName("shouldTearDown checker");
+        t.start();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+            }
+        };
+    }
+}