Improve the SFT log for features.xml/feature.xml
[odlparent.git] / features4-test / src / main / java / org / opendaylight / odlparent / featuretest / PerRepoTestRunner.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.odlparent.featuretest;
10
11 import java.io.IOException;
12 import java.net.URL;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.List;
16 import javax.xml.bind.JAXBException;
17 import org.apache.karaf.features.internal.model.Feature;
18 import org.apache.karaf.features.internal.model.Features;
19 import org.apache.karaf.features.internal.model.JaxbUtil;
20 import org.junit.runner.Description;
21 import org.junit.runner.notification.RunNotifier;
22 import org.junit.runners.ParentRunner;
23 import org.junit.runners.model.InitializationError;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 public class PerRepoTestRunner extends ParentRunner<PerFeatureRunner> {
28
29     private static final Logger LOG = LoggerFactory.getLogger(PerRepoTestRunner.class);
30
31     private static final String REPO_RECURSE = "repo.recurse";
32     private static final String[] FEATURES_FILENAMES = new String[] { "features.xml", "feature.xml" };
33
34     private static boolean isURLStreamHandlerFactorySet = false;
35     // Do NOT static { URL.setURLStreamHandlerFactory(new CustomBundleUrlStreamHandlerFactory()); }
36     // This is VERY BAD practice, because it leads to VERY HARD TO TRACK errors in case ANYTHING
37     // goes wrong in this.  For example, we had a case where (following an upgrade of PAX Exam)
38     // a dependency was missing.  This appeared as a confusing error because the root cause
39     // of static initialization errors is typically lost in Java; so best is NOT to use it!
40     // ("NoClassDefFoundError: Could not initialize class ...PerRepoTestRunner")
41
42     private final List<PerFeatureRunner> children = new ArrayList<>();
43
44     /**
45      * Create a runner.
46      *
47      * @param testClass The test class.
48      * @throws InitializationError if an error occurs.
49      */
50     public PerRepoTestRunner(final Class<?> testClass) throws InitializationError {
51         super(testClass);
52         setURLStreamHandlerFactory();
53         try {
54             for (String filename : FEATURES_FILENAMES) {
55                 final URL repoUrl = getClass().getClassLoader().getResource(filename);
56                 if (repoUrl != null) {
57                     final boolean recursive = Boolean.getBoolean(REPO_RECURSE);
58                     LOG.info("Creating test runners for repoUrl {} recursive {}", repoUrl, recursive);
59                     children.addAll(runnersFromRepoUrl(repoUrl, testClass, recursive));
60                 }
61             }
62             if (children.isEmpty()) {
63                 LOG.error("No features found to test; looked for {}", Arrays.toString(FEATURES_FILENAMES));
64             }
65         } catch (final IOException | JAXBException e) {
66             throw new InitializationError(e);
67         }
68     }
69
70     // We have to exceptionally suppress IllegalCatch just because URL.setURLStreamHandlerFactory stupidly throws Error
71     @SuppressWarnings("checkstyle:IllegalCatch")
72     // see doc on isURLStreamHandlerFactorySet for why we do NOT want to do this in a static block
73     private static synchronized void setURLStreamHandlerFactory() {
74         if (!isURLStreamHandlerFactorySet) {
75             try {
76                 URL.setURLStreamHandlerFactory(new CustomBundleUrlStreamHandlerFactory());
77                 isURLStreamHandlerFactorySet = true;
78             } catch (Error e) {
79                 LOG.warn("Failed to setURLStreamHandlerFactory to CustomBundleUrlStreamHandlerFactory "
80                         + "(depending on which is already set, this may or may not actually be a problem"
81                         + "; e.g. Karaf 4 already registers the neccessary handlers, so OK to ignore)", e);
82             }
83         }
84     }
85
86     protected List<PerFeatureRunner> runnersFromRepoUrl(
87             final URL repoUrl, final Class<?> testClass, final boolean recursive)
88             throws JAXBException, IOException, InitializationError {
89         if (recursive) {
90             return recursiveRunnersFromRepoUrl(repoUrl, testClass);
91         } else {
92             return runnersFromRepoUrl(repoUrl, testClass);
93         }
94     }
95
96     protected List<PerFeatureRunner> runnersFromRepoUrl(final URL repoUrl, final Class<?> testClass)
97             throws JAXBException, IOException, InitializationError {
98         final List<PerFeatureRunner> runners = new ArrayList<>();
99         final Features features = getFeatures(repoUrl);
100         runners.addAll(runnersFromFeatures(repoUrl, features, testClass));
101         return runners;
102     }
103
104     protected List<PerFeatureRunner> recursiveRunnersFromRepoUrl(final URL repoUrl, final Class<?> testClass)
105             throws JAXBException, IOException, InitializationError {
106         final List<PerFeatureRunner> runners = new ArrayList<>();
107         final Features features = getFeatures(repoUrl);
108         runners.addAll(runnersFromRepoUrl(repoUrl, testClass));
109         for (final String repoString : features.getRepository()) {
110             final URL subRepoUrl = new URL(repoString);
111             runners.addAll(recursiveRunnersFromRepoUrl(subRepoUrl, testClass));
112         }
113         return runners;
114     }
115
116     protected List<PerFeatureRunner> runnersFromFeatures(
117             final URL repoUrl, final Features features, final Class<?> testClass) throws InitializationError {
118         final List<PerFeatureRunner> runners = new ArrayList<>();
119         final List<Feature> featureList = features.getFeature();
120         for (final Feature f : featureList) {
121             runners.add(new PerFeatureRunner(repoUrl, f.getName(), f.getVersion(), testClass));
122         }
123         return runners;
124     }
125
126     protected Features getFeatures(final URL repoUrl) throws JAXBException, IOException {
127         return JaxbUtil.unmarshal(repoUrl.toExternalForm(), false);
128     }
129
130     @Override
131     protected List<PerFeatureRunner> getChildren() {
132         return children;
133     }
134
135     @Override
136     protected Description describeChild(final PerFeatureRunner child) {
137         return child.getDescription();
138     }
139
140     @Override
141     @SuppressWarnings("checkstyle:RegexpSinglelineJava")
142     protected void runChild(final PerFeatureRunner child, final RunNotifier notifier) {
143         LOG.info("[LOG] About to run test: {}", child.getDescription());
144         System.out.println("[sys.out] About to run test: " + child.getDescription());
145         child.run(notifier);
146     }
147
148     /* (non-Javadoc)
149      * @see org.junit.runner.Runner#testCount()
150      */
151     @Override
152     public int testCount() {
153         return super.testCount() * children.size();
154     }
155 }