001/* 002 * Units of Measurement TCK 003 * Copyright © 2005-2023, 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.5, October 31, 2023 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 /** Section prefix */ public static final String SECTION_PREFIX = "Section "; 098 /** JSR Base package */ public static final String MEASURE_PACKAGE = "javax.measure"; 099 100 // TCK Constants 101 /** The Spec ID */ public static final String SPEC_ID = "JSR 385"; 102 /** The Spec Version */ public static final String SPEC_VERSION = "2.2"; 103 /** The TCK Version */ private static final String TCK_VERSION = "2.2"; 104 105 private static final String MSG_FAILED = "[FAILED] "; 106 107 private final Profile profile; 108 109 /** Default constructor */ 110 public TCKRunner() { 111 setName(SPEC_ID + " - TCK " + TCK_VERSION); 112 final XmlTest test = new XmlTest(this); 113 profile = Profile.valueOf((System.getProperty(SYS_PROPERTY_PROFILE, 114 Profile.FULL.name()).toUpperCase())); 115 for (String group : profile.getGroups()) { 116 test.addIncludedGroup(group); 117 } 118 test.setName("TCK/Test Setup"); 119 final List<XmlClass> classes = new ArrayList<>(); 120 classes.add(new XmlClass(TCKSetup.class)); 121 classes.add(new XmlClass(FundamentalTypesTest.class)); 122 classes.add(new XmlClass(UnitInterfaceTest.class)); 123 classes.add(new XmlClass(UnitConversionTest.class)); 124 classes.add(new XmlClass(PrefixInterfaceTest.class)); 125 classes.add(new XmlClass(UnitDimensionTest.class)); 126 classes.add(new XmlClass(QuantityInterfaceTest.class)); 127 classes.add(new XmlClass(QuantityTypesTest.class)); 128 classes.add(new XmlClass(UnitFormatTest.class)); 129 classes.add(new XmlClass(QuantityFormatTest.class)); 130 classes.add(new XmlClass(QuantityFactoryTest.class)); 131 classes.add(new XmlClass(SystemOfUnitsTest.class)); 132 classes.add(new XmlClass(ServiceProviderTest.class)); 133 classes.add(new XmlClass(ServicesTest.class)); 134 classes.add(new XmlClass(ObtainingUnitsTest.class)); 135 classes.add(new XmlClass(ObtainingQuantiesTest.class)); 136 test.setXmlClasses(classes); 137 } 138 139 /** 140 * Main method to start the TCK. Optional arguments are: 141 * <ul> 142 * <li>-Dtech.units.tck.profile for defining the profile for TestNG groups (default: full).</li> 143 * <li>-Dtech.units.tck.outputDir for defining the output directory TestNG uses (default: 144 * ./target/tck-output).</li> 145 * <li>-Dtech.units.tck.verbose=true to enable TestNG verbose mode.</li> 146 * <li>-Dtech.units.tck.reportFile=targetFile.txt for defining the TCK result summary report 147 * target file (default: ./target/tck-results.txt).</li> 148 * </ul> 149 * 150 * @param args Optional arguments to control TCK execution 151 */ 152 @Override 153 public int run(InputStream in, OutputStream out, OutputStream err, String... args) { 154 System.out.println("-- " + SPEC_ID + " TCK started --"); 155 System.out.println("Profile: " + profile.getDescription()); 156 final List<XmlSuite> suites = new ArrayList<>(); 157 suites.add(new TCKRunner()); 158 final TestNG tng = new TestNG(); 159 tng.setXmlSuites(suites); 160 String outDir = System.getProperty(SYS_PROPERTY_OUTPUT_DIR, "./target/tck-output"); 161 tng.setOutputDirectory(outDir); 162 String verbose = System.getProperty(SYS_PROPERTY_VERBOSE); 163 if ("true".equalsIgnoreCase(verbose)) { 164 tng.addListener(new VerboseReporter("[VerboseUoM] ")); 165 } 166 String reportFile = System.getProperty(SYS_PROPERTY_REPORT_FILE, "./target/tck-results.txt"); 167 final File file = new File(reportFile); 168 final Reporter rep = new Reporter(profile, file); 169 System.out.println("Writing to file " + file.getAbsolutePath() + " ..."); 170 tng.addListener(rep); 171 tng.run(); 172 rep.writeSummary(); 173 System.out.println("-- " + SPEC_ID + " TCK finished --"); 174 return 0; 175 } 176 177 @Override 178 public String getVersion() { 179 return TCK_VERSION; 180 } 181 182 @SuppressWarnings("exports") 183 @Override 184 public final Set<SourceVersion> getSourceVersions() { 185 return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new SourceVersion[] { 186 SourceVersion.RELEASE_8}))); 187 } 188 189 /** Main method 190 * @param args the arguments 191 */ 192 public static final void main(String... args) { 193 if (args.length > 0 && "-version".equalsIgnoreCase(args[0])) { 194 showVersion(); 195 } else { 196 showHelp(); 197 } 198 } 199 200 private static void showHelp() { 201 final StringWriter consoleWriter = new StringWriter(1000); 202 consoleWriter.write("*****************************************************************************************\n"); 203 consoleWriter.write("**** " + SPEC_ID + " - Units of Measurement, Technical Compatibility Kit, version " + TCK_VERSION + "\n"); 204 consoleWriter.write("*****************************************************************************************\n\n"); 205 consoleWriter.write("Usage:\n"); 206 consoleWriter.write("To run the TCK, execute TestNG with Maven or a similar build tool.\n\n"); 207 consoleWriter.write("E.g. by running \"mvn test\" with this POM.\n\n"); 208 consoleWriter.write("You may use the following system properties to override the default behavior:\n"); 209 consoleWriter.write("-D" + SYS_PROPERTY_PROFILE + "=<profile>" + " to select the desired profile from these available " + SPEC_ID + " profiles:\n"); 210 for (Profile p : Profile.values()) { 211 consoleWriter.write(" " + p.name() + " - " + p.getDescription() + (p.isDefault() ? " (the default profile)\n" : "\n")); 212 } 213 consoleWriter.write("-D" + SYS_PROPERTY_OUTPUT_DIR + "=<directory> to set the output directory of your choice.\n"); 214 consoleWriter.write("-D" + SYS_PROPERTY_REPORT_FILE + "=<file> to set the TCK result file directory of your choice.\n"); 215 consoleWriter.write("-D" + SYS_PROPERTY_VERBOSE + "=true/false to toggle the TCK verbose option for additional test output. The default is \"false\"\n"); 216 System.out.println(consoleWriter); 217 } 218 219 private static void showVersion() { 220 System.out.println(SPEC_ID + " - Units of Measurement, Technical Compatibility Kit, version \"" + TCK_VERSION + "\"\n"); 221 } 222 223 /** The Test Reporter */ 224 private static final class Reporter extends TestListenerAdapter { 225 private int count = 0; 226 private int skipped = 0; 227 private int failed = 0; 228 private int success = 0; 229 private final StringWriter consoleWriter = new StringWriter(3000); 230 private FileWriter fileWriter; 231 232 public Reporter(Profile profile, File file) { 233 try { 234 if (!file.exists()) { 235 file.createNewFile(); 236 } 237 fileWriter = new FileWriter(file); 238 fileWriter.write("*****************************************************************************************\n"); 239 fileWriter.write("**** " + SPEC_ID + " - Units of Measurement, Technical Compatibility Kit, version " + TCK_VERSION + "\n"); 240 fileWriter.write("*****************************************************************************************\n\n"); 241 fileWriter.write("Executed on " + new java.util.Date() + "\n"); 242 fileWriter.write("Operating System " 243 + System.getProperty("os.name") + " (" 244 + System.getProperty("os.version") + ", " 245 + System.getProperty("os.arch") + ") \n"); 246 fileWriter.write("Java " 247 + System.getProperty("java.version") + " (" 248 + System.getProperty("java.vendor") + ") \n"); 249 fileWriter.write("Using " + profile.getDescription() + " profile\n\n"); 250 // System.out: 251 consoleWriter.write("*****************************************************************************************\n"); 252 consoleWriter.write("**** " + SPEC_ID + " - Units of Measurement, Technical Compatibility Kit, version " + TCK_VERSION + "\n"); 253 consoleWriter.write("*****************************************************************************************\n\n"); 254 consoleWriter.write("Executed on " + new java.util.Date() + "\n"); 255 consoleWriter.write("Using " + profile.getDescription() + " profile\n\n"); 256 } catch (IOException e) { 257 e.printStackTrace(); 258 System.exit(-1); 259 } 260 } 261 262 @Override 263 public void onTestFailure(ITestResult tr) { 264 failed++; 265 count++; 266 String location = tr.getTestClass().getRealClass().getSimpleName() + '#' + tr.getMethod().getMethodName(); 267 try { 268 Method realTestMethod = tr.getMethod().getConstructorOrMethod().getMethod(); 269 Test testAnnot = realTestMethod.getAnnotation(Test.class); 270 if (testAnnot != null && testAnnot.description() != null && !testAnnot.description().isEmpty()) { 271 if (tr.getThrowable() != null) { 272 StringWriter sw = new StringWriter(); 273 PrintWriter w = new PrintWriter(sw); 274 tr.getThrowable().printStackTrace(w); 275 w.flush(); 276 log(MSG_FAILED + testAnnot.description() + "(" + location + "):\n" + sw.toString()); 277 } else { 278 log(MSG_FAILED + testAnnot.description() + "(" + location + ")"); 279 } 280 } else { 281 if (tr.getThrowable() != null) { 282 StringWriter sw = new StringWriter(); 283 PrintWriter w = new PrintWriter(sw); 284 tr.getThrowable().printStackTrace(w); 285 w.flush(); 286 log(MSG_FAILED + location + ":\n" + sw.toString()); 287 } else { 288 log(MSG_FAILED + location); 289 } 290 } 291 } catch (IOException e) { 292 throw new IllegalStateException("IO Error", e); 293 } 294 } 295 296 @Override 297 public void onTestSkipped(ITestResult tr) { 298 skipped++; 299 count++; 300 String location = tr.getTestClass().getRealClass().getSimpleName() + '#' + tr.getMethod().getMethodName(); 301 try { 302 Method realTestMethod = tr.getMethod().getConstructorOrMethod().getMethod(); 303 Test specAssert = realTestMethod.getAnnotation(Test.class); 304 if (specAssert != null && specAssert.description() != null && !specAssert.description().isEmpty()) { 305 log("[SKIPPED] " + specAssert.description() + "(" + location + ")"); 306 } else { 307 log("[SKIPPED] " + location); 308 } 309 } catch (IOException e) { 310 throw new IllegalStateException("IO Error", e); 311 } 312 } 313 314 @Override 315 public void onTestSuccess(ITestResult tr) { 316 success++; 317 count++; 318 String location = tr.getTestClass().getRealClass().getSimpleName() + '#' + tr.getMethod().getMethodName(); 319 try { 320 Method realTestMethod = tr.getMethod().getConstructorOrMethod().getMethod(); 321 Test specAssert = realTestMethod.getAnnotation(Test.class); 322 if (specAssert != null && specAssert.description() != null && !specAssert.description().isEmpty()) { 323 log("[SUCCESS] " + specAssert.description() + "(" + location + ")"); 324 } else { 325 log("[SUCCESS] " + location); 326 } 327 } catch (IOException e) { 328 throw new IllegalStateException("IO Error", e); 329 } 330 } 331 332 private void log(String text) throws IOException { 333 fileWriter.write(text); 334 fileWriter.write('\n'); 335 consoleWriter.write(text); 336 consoleWriter.write('\n'); 337 } 338 339 public void writeSummary() { 340 try { 341 log("\n" + SPEC_ID + " TCK version " + TCK_VERSION + " Summary"); 342 log("-------------------------------------------------------"); 343 log("\nTOTAL TESTS EXECUTED : " + count); 344 log("TOTAL TESTS SKIPPED : " + skipped); 345 log("TOTAL TESTS SUCCESS : " + success); 346 log("TOTAL TESTS FAILED : " + failed); 347 fileWriter.flush(); 348 fileWriter.close(); 349 consoleWriter.flush(); 350 System.out.println(); 351 System.out.println(consoleWriter); 352 } catch (IOException e) { 353 throw new IllegalStateException("IO Error", e); 354 } 355 } 356 } 357}