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 2017/10/08 12:02:53 UTC

[10/13] camel git commit: Camel route coverage maven plugin

Camel route coverage maven plugin


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/760f05cf
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/760f05cf
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/760f05cf

Branch: refs/heads/parser2
Commit: 760f05cf90280a6819546001e49fbd024d53202e
Parents: 4cf26c0
Author: Claus Ibsen <da...@apache.org>
Authored: Sun Oct 8 13:05:17 2017 +0200
Committer: Claus Ibsen <da...@apache.org>
Committed: Sun Oct 8 13:05:17 2017 +0200

----------------------------------------------------------------------
 .../java/sample/camel/SampleCamelRouter.java    |   3 +
 .../java/sample/camel/FooApplicationTest.java   |  53 +++++++++
 .../org/apache/camel/maven/CoverageMojo.java    |  98 ++++++++++++++--
 .../camel/maven/helper/CoverageHelper.java      | 114 +++++++++++++++++++
 .../apache/camel/maven/model/CoverageNode.java  |  58 ++++++++++
 5 files changed, 318 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/760f05cf/examples/camel-example-spring-boot/src/main/java/sample/camel/SampleCamelRouter.java
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot/src/main/java/sample/camel/SampleCamelRouter.java b/examples/camel-example-spring-boot/src/main/java/sample/camel/SampleCamelRouter.java
index 20cf957..d8e7d2e 100644
--- a/examples/camel-example-spring-boot/src/main/java/sample/camel/SampleCamelRouter.java
+++ b/examples/camel-example-spring-boot/src/main/java/sample/camel/SampleCamelRouter.java
@@ -31,6 +31,9 @@ public class SampleCamelRouter extends RouteBuilder {
     public void configure() throws Exception {
         from("timer:hello?period={{timer.period}}").routeId("hello")
                 .transform(method("myBean", "saySomething"))
+                .filter(simple("${body} contains 'foo'"))
+                    .to("log:foo")
+                .end()
                 .to("stream:out");
     }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/760f05cf/examples/camel-example-spring-boot/src/test/java/sample/camel/FooApplicationTest.java
----------------------------------------------------------------------
diff --git a/examples/camel-example-spring-boot/src/test/java/sample/camel/FooApplicationTest.java b/examples/camel-example-spring-boot/src/test/java/sample/camel/FooApplicationTest.java
new file mode 100644
index 0000000..0b76960
--- /dev/null
+++ b/examples/camel-example-spring-boot/src/test/java/sample/camel/FooApplicationTest.java
@@ -0,0 +1,53 @@
+/**
+ * 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 sample.camel;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.test.spring.CamelSpringBootRunner;
+import org.apache.camel.test.spring.RouteCoverage;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(CamelSpringBootRunner.class)
+@SpringBootTest(classes = SampleCamelApplication.class,
+    properties = "greeting = Hell foo")
+@RouteCoverage
+@Ignore
+public class FooApplicationTest {
+
+    @Autowired
+    private CamelContext camelContext;
+
+    @Test
+    public void shouldSayFoo() throws Exception {
+        // we expect that one or more messages is automatic done by the Camel
+        // route as it uses a timer to trigger
+        NotifyBuilder notify = new NotifyBuilder(camelContext).whenDone(1).create();
+
+        assertTrue(notify.matches(10, TimeUnit.SECONDS));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/760f05cf/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/CoverageMojo.java
----------------------------------------------------------------------
diff --git a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/CoverageMojo.java b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/CoverageMojo.java
index e529587..ef1c8cd 100644
--- a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/CoverageMojo.java
+++ b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/CoverageMojo.java
@@ -16,19 +16,26 @@
  */
 package org.apache.camel.maven;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.PrintStream;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
+import org.apache.camel.maven.helper.CoverageHelper;
 import org.apache.camel.maven.helper.EndpointHelper;
+import org.apache.camel.maven.model.CoverageNode;
 import org.apache.camel.parser.RouteBuilderParser;
 import org.apache.camel.parser.model.CamelEndpointDetails;
 import org.apache.camel.parser.model.CamelNodeDetails;
 import org.apache.camel.parser.model.CamelRouteDetails;
 import org.apache.camel.parser.model.CamelSimpleExpressionDetails;
+import org.apache.camel.util.KeyValueHolder;
 import org.apache.maven.model.Resource;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
@@ -168,10 +175,21 @@ public class CoverageMojo extends AbstractExecMojo {
         routeTrees.forEach(t -> {
             String routeId = t.getRouteId();
             String fileName = asRelativeFile(t.getFileName());
-            String tree = t.dump(4);
 
-            getLog().info("Route " + routeId + " discovered in file " + fileName);
-            getLog().info("\n" + tree + "\n");
+            // grab dump data for the route
+            try {
+                List<KeyValueHolder<String, Integer>> coverageData = CoverageHelper.parseDumpRouteCoverageByRouteId("target/camel-route-coverage", routeId);
+
+                List<CoverageNode> coverage = gatherRouteCoverageSummary(t, coverageData);
+
+                String out = templateCoverageData(fileName, routeId, coverage);
+                getLog().info("Route coverage summary:\n\n" + out);
+                getLog().info("");
+
+            } catch (Exception e) {
+                e.printStackTrace();
+                // ignore
+            }
         });
 
         int notCovered = 0;
@@ -182,16 +200,80 @@ public class CoverageMojo extends AbstractExecMojo {
     }
     // CHECKSTYLE:ON
 
-    private static int countRouteId(List<CamelRouteDetails> details, String routeId) {
-        int answer = 0;
-        for (CamelRouteDetails detail : details) {
-            if (routeId.equals(detail.getRouteId())) {
-                answer++;
+    @SuppressWarnings("unchecked")
+    private String templateCoverageData(String fileName, String routeId, List<CoverageNode> model) throws MojoExecutionException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        PrintStream sw = new PrintStream(bos);
+
+        sw.println("File: " + fileName);
+        sw.println("Route: " + routeId);
+        sw.println();
+        sw.println(String.format("%8s   %8s   %s", "Line #", "Count", "Route"));
+        sw.println(String.format("%8s   %8s   %s", "------", "-----", "-----"));
+
+        int covered = 0;
+        for (CoverageNode node : model) {
+            if (node.getCount() > 0) {
+                covered++;
             }
+            String pad = padString(node.getLevel());
+            sw.println(String.format("%8s   %8s   %s", node.getLineNumber(), node.getCount(), pad + node.getName()));
         }
+
+        // calculate percentage of route coverage (must use double to have decimals)
+        double percentage = ((double) covered / (double) model.size()) * 100;
+        sw.println();
+        sw.println("Coverage: " + covered + " out of " + model.size() + " (" + String.format("%.1f", percentage) + "%)");
+        sw.println();
+
+        return bos.toString();
+    }
+
+    private static List<CoverageNode> gatherRouteCoverageSummary(CamelNodeDetails route, List<KeyValueHolder<String, Integer>> coverageData) {
+        List<CoverageNode> answer = new ArrayList<>();
+
+        Iterator<KeyValueHolder<String, Integer>> it = coverageData.iterator();
+        AtomicInteger level = new AtomicInteger();
+        gatherRouteCoverageSummary(route, it, level, answer);
         return answer;
     }
 
+    private static void gatherRouteCoverageSummary(CamelNodeDetails node, Iterator<KeyValueHolder<String, Integer>> it, AtomicInteger level, List<CoverageNode> answer) {
+        CoverageNode data = new CoverageNode();
+        data.setName(node.getName());
+        data.setLineNumber(node.getLineNumber());
+        data.setLevel(level.get());
+
+        // add data
+        answer.add(data);
+
+        // find count
+        boolean found = false;
+        while (!found && it.hasNext()) {
+            KeyValueHolder<String, Integer> holder = it.next();
+            found = holder.getKey().equals(node.getName());
+            if (found) {
+                data.setCount(holder.getValue());
+            }
+        }
+
+        if (node.getOutputs() != null) {
+            level.addAndGet(1);
+            for (CamelNodeDetails child : node.getOutputs()) {
+                gatherRouteCoverageSummary(child, it, level, answer);
+            }
+            level.addAndGet(-1);
+        }
+    }
+
+    private static String padString(int level) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < level; i++) {
+            sb.append("  ");
+        }
+        return sb.toString();
+    }
+
     private void findJavaFiles(File dir, Set<File> javaFiles) {
         File[] files = dir.isDirectory() ? dir.listFiles() : null;
         if (files != null) {

http://git-wip-us.apache.org/repos/asf/camel/blob/760f05cf/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/helper/CoverageHelper.java
----------------------------------------------------------------------
diff --git a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/helper/CoverageHelper.java b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/helper/CoverageHelper.java
new file mode 100644
index 0000000..33913d4
--- /dev/null
+++ b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/helper/CoverageHelper.java
@@ -0,0 +1,114 @@
+/**
+ * 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.maven.helper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.KeyValueHolder;
+import org.apache.camel.util.XmlLineNumberParser;
+
+public final class CoverageHelper {
+
+    private CoverageHelper() {
+    }
+
+    public static List<KeyValueHolder<String, Integer>> parseDumpRouteCoverageByRouteId(String directory, String routeId) throws Exception {
+        List<KeyValueHolder<String, Integer>> answer = new ArrayList<>();
+
+        File[] files = new File(directory).listFiles(f -> f.getName().endsWith(".xml"));
+        if (files == null) {
+            return answer;
+        }
+
+        CamelCatalog catalog = new DefaultCamelCatalog(true);
+
+        for (File file : files) {
+            FileInputStream fis = new FileInputStream(file);
+            Document dom = XmlLineNumberParser.parseXml(fis);
+            IOHelper.close(fis);
+            NodeList routes = dom.getElementsByTagName("route");
+            for (int i = 0; i < routes.getLength(); i++) {
+                Node route = routes.item(i);
+                String id = route.getAttributes().getNamedItem("id").getNodeValue();
+                // must be the target route
+                if (routeId.equals(id)) {
+                    // parse each route and build a Map<String, Integer> with the no of messages processed
+                    // where String is the EIP name
+                    AtomicInteger counter = new AtomicInteger();
+                    parseRouteData(catalog, route, answer, counter);
+                }
+            }
+        }
+
+        return answer;
+    }
+
+    private static void parseRouteData(CamelCatalog catalog, Node node, List<KeyValueHolder<String, Integer>> data, AtomicInteger counter) {
+        // must be a known EIP model
+        String key = node.getNodeName();
+        boolean valid = catalog.findModelNames().contains(key); // skip route as we use from instead
+        if (!valid) {
+            return;
+        }
+
+        // only calculate for elements within the route
+        if (!"route".equals(key)) {
+            Integer count = 0;
+            Node total = node.getAttributes().getNamedItem("exchangesTotal");
+            if (total != null) {
+                count = Integer.valueOf(total.getNodeValue());
+            }
+            KeyValueHolder<String, Integer> holder = data.size() > counter.get() ? data.get(counter.get()) : null;
+            if (holder != null && holder.getKey().equals(key)) {
+                count += holder.getValue();
+            }
+            if (holder == null) {
+                // add new
+                data.add(counter.get(), new KeyValueHolder<>(key, count));
+            } else {
+                // replace existing
+                data.set(counter.get(), new KeyValueHolder<>(key, count));
+            }
+            // advance counter
+            counter.incrementAndGet();
+        }
+
+        // any children
+        NodeList children = node.getChildNodes();
+        if (children != null) {
+            for (int i = 0; i < children.getLength(); i++) {
+                Node child = children.item(i);
+                if (child instanceof Element) {
+                    parseRouteData(catalog, child, data, counter);
+                }
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/760f05cf/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/model/CoverageNode.java
----------------------------------------------------------------------
diff --git a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/model/CoverageNode.java b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/model/CoverageNode.java
new file mode 100644
index 0000000..1e749cb
--- /dev/null
+++ b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/model/CoverageNode.java
@@ -0,0 +1,58 @@
+/**
+ * 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.maven.model;
+
+public final class CoverageNode {
+
+    private String name;
+    private String lineNumber;
+    private Integer count;
+    private int level;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getLineNumber() {
+        return lineNumber;
+    }
+
+    public void setLineNumber(String lineNumber) {
+        this.lineNumber = lineNumber;
+    }
+
+    public Integer getCount() {
+        return count;
+    }
+
+    public void setCount(Integer count) {
+        this.count = count;
+    }
+
+    public int getLevel() {
+        return level;
+    }
+
+    public void setLevel(int level) {
+        this.level = level;
+    }
+
+}