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;
+ }
+
+}