001/*
002 * Units of Measurement TCK
003 * Copyright © 2005-2020, Jean-Marie Dautelle, Werner Keil, Otavio Santana.
004 *
005 * All rights reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without modification,
008 * are permitted provided that the following conditions are met:
009 *
010 * 1. Redistributions of source code must retain the above copyright notice,
011 *    this list of conditions and the following disclaimer.
012 *
013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
014 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
015 *
016 * 3. Neither the name of JSR-385 nor the names of its contributors may be used to endorse or promote products
017 *    derived from this software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package tech.units.tck;
031
032import static tech.units.tck.util.TestUtils.SYS_PROPERTY_OUTPUT_DIR;
033import static tech.units.tck.util.TestUtils.SYS_PROPERTY_PROFILE;
034import static tech.units.tck.util.TestUtils.SYS_PROPERTY_REPORT_FILE;
035import static tech.units.tck.util.TestUtils.SYS_PROPERTY_VERBOSE;
036
037import java.io.File;
038import java.io.FileWriter;
039import java.io.IOException;
040import java.io.InputStream;
041import java.io.OutputStream;
042import java.io.PrintWriter;
043import java.io.StringWriter;
044import java.lang.reflect.Method;
045import java.util.ArrayList;
046import java.util.Arrays;
047import java.util.Collections;
048import java.util.HashSet;
049import java.util.List;
050import java.util.Set;
051
052import javax.lang.model.SourceVersion;
053import javax.tools.Tool;
054
055import org.testng.ITestResult;
056import org.testng.TestListenerAdapter;
057import org.testng.TestNG;
058import org.testng.annotations.Test;
059import org.testng.reporters.VerboseReporter;
060import org.testng.xml.XmlClass;
061import org.testng.xml.XmlSuite;
062import org.testng.xml.XmlTest;
063
064import tech.units.tck.tests.FundamentalTypesTest;
065import tech.units.tck.tests.format.QuantityFormatTest;
066import tech.units.tck.tests.format.UnitFormatTest;
067import tech.units.tck.tests.quantity.QuantityInterfaceTest;
068import tech.units.tck.tests.quantity.QuantityTypesTest;
069import tech.units.tck.tests.spi.ObtainingQuantiesTest;
070import tech.units.tck.tests.spi.ObtainingUnitsTest;
071import tech.units.tck.tests.spi.QuantityFactoryTest;
072import tech.units.tck.tests.spi.ServiceProviderTest;
073import tech.units.tck.tests.spi.ServicesTest;
074import tech.units.tck.tests.spi.SystemOfUnitsTest;
075import tech.units.tck.tests.unit.PrefixInterfaceTest;
076import tech.units.tck.tests.unit.UnitConversionTest;
077import tech.units.tck.tests.unit.UnitDimensionTest;
078import tech.units.tck.tests.unit.UnitInterfaceTest;
079import tech.units.tck.util.TestGroups.Profile;
080import tech.uom.lib.common.function.Versioned;
081
082/**
083 * Main class for executing the JSR 385 TCK.
084 * 
085 * @author <a href="mailto:[email protected]">Werner Keil</a>
086 * @version 2.2, November 15, 2020
087 * @since 1.0
088 */
089public class TCKRunner extends XmlSuite implements Tool, Versioned<String> {
090
091    /**
092     * 
093     */
094    //private static final long serialVersionUID = 3189431432291353154L;
095    
096        // General String Constants
097        public static final String SECTION_PREFIX = "Section ";
098        public static final String MEASURE_PACKAGE = "javax.measure";
099        
100        // TCK Constants
101    public static final String SPEC_ID = "JSR 385";
102    public static final String SPEC_VERSION = "2.1.0";
103        private static final String TCK_VERSION = "2.1.0";
104        
105    private final Profile profile;
106
107    public TCKRunner() {
108        setName(SPEC_ID + " - TCK " + TCK_VERSION);
109        final XmlTest test = new XmlTest(this);
110        profile = Profile.valueOf((System.getProperty(SYS_PROPERTY_PROFILE, 
111                        Profile.FULL.name()).toUpperCase()));
112        for (String group : profile.getGroups()) {
113            test.addIncludedGroup(group);
114        }
115        test.setName("TCK/Test Setup");
116        final List<XmlClass> classes = new ArrayList<>();
117        classes.add(new XmlClass(TCKSetup.class));
118        classes.add(new XmlClass(FundamentalTypesTest.class));
119        classes.add(new XmlClass(UnitInterfaceTest.class));
120        classes.add(new XmlClass(UnitConversionTest.class));
121        classes.add(new XmlClass(PrefixInterfaceTest.class));
122        classes.add(new XmlClass(UnitDimensionTest.class));
123        classes.add(new XmlClass(QuantityInterfaceTest.class));
124        classes.add(new XmlClass(QuantityTypesTest.class));
125        classes.add(new XmlClass(UnitFormatTest.class));
126        classes.add(new XmlClass(QuantityFormatTest.class));
127        classes.add(new XmlClass(QuantityFactoryTest.class));
128        classes.add(new XmlClass(SystemOfUnitsTest.class));
129        classes.add(new XmlClass(ServiceProviderTest.class));
130        classes.add(new XmlClass(ServicesTest.class));
131        classes.add(new XmlClass(ObtainingUnitsTest.class));
132        classes.add(new XmlClass(ObtainingQuantiesTest.class));
133        test.setXmlClasses(classes);
134    }
135
136    /**
137     * Main method to start the TCK. Optional arguments are:
138     * <ul>
139     * <li>-Dtech.units.tck.profile for defining the profile for TestNG groups (default: full).</li>
140     * <li>-Dtech.units.tck.outputDir for defining the output directory TestNG uses (default:
141     * ./target/tck-output).</li>
142     * <li>-Dtech.units.tck.verbose=true to enable TestNG verbose mode.</li>
143     * <li>-Dtech.units.tck.reportFile=targetFile.txt for defining the TCK result summary report
144     * target file (default: ./target/tck-results.txt).</li>
145     * </ul>
146     * 
147     * @param args Optional arguments to control TCK execution
148     */
149    @Override
150    public int run(InputStream in, OutputStream out, OutputStream err, String... args) {
151        System.out.println("-- " + SPEC_ID + " TCK started --");
152        System.out.println("Profile: " + profile.getDescription());
153        final List<XmlSuite> suites = new ArrayList<>();
154        suites.add(new TCKRunner());
155        final TestNG tng = new TestNG();
156        tng.setXmlSuites(suites);
157        String outDir = System.getProperty(SYS_PROPERTY_OUTPUT_DIR, "./target/tck-output");
158        tng.setOutputDirectory(outDir);
159        String verbose = System.getProperty(SYS_PROPERTY_VERBOSE);
160        if ("true".equalsIgnoreCase(verbose)) {
161            tng.addListener(new VerboseReporter("[VerboseUoM] "));
162        }
163        String reportFile = System.getProperty(SYS_PROPERTY_REPORT_FILE, "./target/tck-results.txt");
164        final File file = new File(reportFile);
165        final Reporter rep = new Reporter(profile, file);
166        System.out.println("Writing to file " + file.getAbsolutePath() + " ...");
167        tng.addListener(rep);
168        tng.run();
169        rep.writeSummary();
170        System.out.println("-- " + SPEC_ID + " TCK finished --");
171        return 0;
172    }
173
174    @Override
175    public String getVersion() {
176        return TCK_VERSION;
177    }
178
179    @Override
180    public final Set<SourceVersion> getSourceVersions() {
181        return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new SourceVersion[] {SourceVersion.RELEASE_5, SourceVersion.RELEASE_6, SourceVersion.RELEASE_7})));
182    }
183
184    public static final void main(String... args) {
185        if (args.length > 0 && "-version".equalsIgnoreCase(args[0])) {
186            showVersion();
187        } else { // (args.length > 0 && "-help".equalsIgnoreCase(args[0])) {
188            showHelp();
189        } /*
190           * else { final Tool runner = new TCKRunner(); runner.run(System.in, System.out,
191           * System.err, new String[]{TCKRunner.class.getName()}); }
192           */
193    }
194
195    private static void showHelp() {
196        final StringWriter consoleWriter = new StringWriter(1000);
197        consoleWriter.write("*****************************************************************************************\n");
198        consoleWriter.write("**** " + SPEC_ID + " - Units of Measurement, Technical Compatibility Kit, version " + TCK_VERSION + "\n");
199        consoleWriter.write("*****************************************************************************************\n\n");
200        consoleWriter.write("Usage:\n");
201        consoleWriter.write("To run the TCK, execute TestNG with Maven or a similar build tool.\n\n");
202        consoleWriter.write("E.g. by running \"mvn test\" with this POM.\n\n");
203        consoleWriter.write("You may use the following system properties to override the default behavior:\n");
204        consoleWriter.write("-D" + SYS_PROPERTY_PROFILE + "=<profile>" + " to select the desired profile from these available " + SPEC_ID + " profiles:\n");
205        for (Profile p : Profile.values()) {
206            consoleWriter.write("   " + p.name() + " - " + p.getDescription() + (p.isDefault() ? " (the default profile)\n" : "\n"));
207        }
208        consoleWriter.write("-D" + SYS_PROPERTY_OUTPUT_DIR + "=<directory> to set the output directory of your choice.\n");
209        consoleWriter.write("-D" + SYS_PROPERTY_REPORT_FILE + "=<file> to set the TCK result file directory of your choice.\n");
210        consoleWriter.write("-D" + SYS_PROPERTY_VERBOSE + "=true/false to toggle the TCK verbose option for additional test output. The default is \"false\"\n");
211        System.out.println(consoleWriter);
212    }
213
214    private static void showVersion() {
215        System.out.println(SPEC_ID + " - Units of Measurement, Technical Compatibility Kit, version \"" + TCK_VERSION + "\"\n");
216    }
217
218    public static final class Reporter extends TestListenerAdapter {
219        private int count = 0;
220        private int skipped = 0;
221        private int failed = 0;
222        private int success = 0;
223        private final StringWriter consoleWriter = new StringWriter(3000);
224        private FileWriter fileWriter;
225
226        public Reporter(Profile profile, File file) {
227            try {
228                if (!file.exists()) {
229                    file.createNewFile();
230                }
231                fileWriter = new FileWriter(file);
232                fileWriter.write("*****************************************************************************************\n");
233                fileWriter.write("**** " + SPEC_ID + " - Units of Measurement, Technical Compatibility Kit, version " + TCK_VERSION + "\n");
234                fileWriter.write("*****************************************************************************************\n\n");
235                fileWriter.write("Executed on " + new java.util.Date() + "\n");
236                fileWriter.write("Operating System " 
237                  + System.getProperty("os.name") + " (" 
238                  + System.getProperty("os.version") + ", "
239                  + System.getProperty("os.arch") + ") \n");
240                fileWriter.write("Java " 
241                        + System.getProperty("java.version") + " (" 
242                        + System.getProperty("java.vendor") + ") \n");
243                fileWriter.write("Using " + profile.getDescription() + " profile\n\n");
244                // System.out:
245                consoleWriter.write("*****************************************************************************************\n");
246                consoleWriter.write("**** " + SPEC_ID + " - Units of Measurement, Technical Compatibility Kit, version " + TCK_VERSION + "\n");
247                consoleWriter.write("*****************************************************************************************\n\n");
248                consoleWriter.write("Executed on " + new java.util.Date() + "\n");
249                consoleWriter.write("Using " + profile.getDescription() + " profile\n\n");
250            } catch (IOException e) {
251                e.printStackTrace();
252                System.exit(-1);
253            }
254        }
255
256        @Override
257        public void onTestFailure(ITestResult tr) {
258            failed++;
259            count++;
260            String location = tr.getTestClass().getRealClass().getSimpleName() + '#' + tr.getMethod().getMethodName();
261            try {
262                Method realTestMethod = tr.getMethod().getConstructorOrMethod().getMethod();
263                Test testAnnot = realTestMethod.getAnnotation(Test.class);
264                if (testAnnot != null && testAnnot.description() != null && !testAnnot.description().isEmpty()) {
265                    if (tr.getThrowable() != null) {
266                        StringWriter sw = new StringWriter();
267                        PrintWriter w = new PrintWriter(sw);
268                        tr.getThrowable().printStackTrace(w);
269                        w.flush();
270                        log("[FAILED]  " + testAnnot.description() + "(" + location + "):\n" + sw.toString());
271                    } else {
272                        log("[FAILED]  " + testAnnot.description() + "(" + location + ")");
273                    }
274                } else {
275                    if (tr.getThrowable() != null) {
276                        StringWriter sw = new StringWriter();
277                        PrintWriter w = new PrintWriter(sw);
278                        tr.getThrowable().printStackTrace(w);
279                        w.flush();
280                        log("[FAILED]  " + location + ":\n" + sw.toString());
281                    } else {
282                        log("[FAILED]  " + location);
283                    }
284                }
285            } catch (IOException e) {
286                throw new IllegalStateException("IO Error", e);
287            }
288        }
289
290        @Override
291        public void onTestSkipped(ITestResult tr) {
292            skipped++;
293            count++;
294            String location = tr.getTestClass().getRealClass().getSimpleName() + '#' + tr.getMethod().getMethodName();
295            try {
296                Method realTestMethod = tr.getMethod().getConstructorOrMethod().getMethod();
297                Test specAssert = realTestMethod.getAnnotation(Test.class);
298                if (specAssert != null && specAssert.description() != null && !specAssert.description().isEmpty()) {
299                    log("[SKIPPED] " + specAssert.description() + "(" + location + ")");
300                } else {
301                    log("[SKIPPED] " + location);
302                }
303            } catch (IOException e) {
304                throw new IllegalStateException("IO Error", e);
305            }
306        }
307
308        @Override
309        public void onTestSuccess(ITestResult tr) {
310            success++;
311            count++;
312            String location = tr.getTestClass().getRealClass().getSimpleName() + '#' + tr.getMethod().getMethodName();
313            try {
314                Method realTestMethod = tr.getMethod().getConstructorOrMethod().getMethod();
315                Test specAssert = realTestMethod.getAnnotation(Test.class);
316                if (specAssert != null && specAssert.description() != null && !specAssert.description().isEmpty()) {
317                    log("[SUCCESS] " + specAssert.description() + "(" + location + ")");
318                } else {
319                    log("[SUCCESS] " + location);
320                }
321            } catch (IOException e) {
322                throw new IllegalStateException("IO Error", e);
323            }
324        }
325
326        private void log(String text) throws IOException {
327            fileWriter.write(text);
328            fileWriter.write('\n');
329            consoleWriter.write(text);
330            consoleWriter.write('\n');
331        }
332
333        public void writeSummary() {
334            try {
335                log("\n" + SPEC_ID + " TCK version " + TCK_VERSION + " Summary");
336                log("-------------------------------------------------------");
337                log("\nTOTAL TESTS EXECUTED : " + count);
338                log("TOTAL TESTS SKIPPED  : " + skipped);
339                log("TOTAL TESTS SUCCESS  : " + success);
340                log("TOTAL TESTS FAILED   : " + failed);
341                fileWriter.flush();
342                fileWriter.close();
343                consoleWriter.flush();
344                System.out.println();
345                System.out.println(consoleWriter);
346            } catch (IOException e) {
347                throw new IllegalStateException("IO Error", e);
348            }
349        }
350    }
351}