1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | package Torello.Java.Build; import static Torello.Java.C.*; import Torello.Java.StorageWriter; import Torello.Java.EXCC; import Torello.Java.FileNode; import Torello.Java.FileRW; import Torello.Java.RTC; import Torello.Java.C; import Torello.Java.Additional.ConstantPool; import Torello.Java.Additional.ByteArrClassLoader; import Torello.Java.Additional.Ret3; import Torello.Java.ReadOnly.ReadOnlyList; import Torello.Java.ReadOnly.ReadOnlyArrayList; import Torello.Java.ReadOnly.ROArrayListBuilder; import java.io.IOException; import java.io.File; // This class has a single method (with one, small, static helper method) that just iterates // through a list of BuildPackage instances. It looks for the root package directory, and then // checks if the My/Package/Directory/ has a sub-directory names 'test-files/'. Whenever it // encounters a 'My/Package/Directory/test-files/', it scans that diretory (and all of its // sub-directories) to identify any Java '.class'-Files. // // Each Java '.class'-File is the checked to see whether it implements the interface // 'Torello.Java.Build.TestingClass'. If it does, then its 'runTests(StorageWriter)' method is // invoked, and the results are saved into the Log-File Directory. // // This class *REALLY* isn't Rocket-Science. It sometimes looks (to me) kind of difficult because // of things like "ByteArrclassLoader", and of course the profuse use of the 'FileNode' and // 'Stream' classes. I wrote the 'ByteArrClassLoader' to circumvent having the place all '.class' // files inside of a Directory-Tree Structure that is required to mimic the Package-Name itself. // Though writing the API for 'Torello.Java.Additional.ConstantPool' was quite a bit of work, it // can be a real life-saver in sitatuions exactly like the need to write a 'ByteArrClassLoader' // // If you just accept that loading Class-Files from a directory where the "Package-Name" and the // Directory-Tree are completely and totally un-correlated and disjoing is *THE ONLY REASON* for // needed the ByteArrClassLoader / getDeclaredConstructor / newInstance stuff, then maybe this // class will look as simple as it really is! // // It scans for all '.class' files which are in a 'package/test-files/' directory - INCLUDING ANY // AND ALL SUB-DIRECTORIES - and then loads them into memory (using the 'ConstantPool' and the // specialized Class-Loader). Then, it runs the 'runTests' on any of those classes which the // user has written and implement the 'TestingClass' interface! // // That's the whole thing in a nutshell. // // Oh, and one more thing (i forgot), the "ConstantPool" class isn't actually used inside this // class - it is buried inside the 'ByteArrClassLoader' itself! class RunTests { // ******************************************************************************************** // ******************************************************************************************** // Run Method // ******************************************************************************************** // ******************************************************************************************** @SuppressWarnings({"unchecked", "rawtypes"}) static void run( final ReadOnlyList<BuildPackage> packagesIN, final String logDirName ) throws IOException { final ByteArrClassLoader bacl = new ByteArrClassLoader(); final StorageWriter log = new StorageWriter(); final ReadOnlyList<BuildPackage> packages = mustHaveTestFilesDir(packagesIN); final PkgTextBar textBar = new PkgTextBar(packages, "Testing Package", BGREEN_BKGND, BCYAN); log.sysOut = false; for (BuildPackage bp : packages) { final String logSubDirName = logDirName + bp.fullName + File.separator; final File logDir = new File(logSubDirName); if (! logDir.exists()) logDir.mkdirs(); final ReadOnlyList<Class> testClasses = FileNode .createRoot(bp.pkgRootDirectory + "test-files" + File.separator) .loadTree(-1, FileNode.CLASS_FILES, null) .flattenJustFiles(RTC.FULLPATH_STREAM()) .map((String fileName) -> (Class) bacl.readJavaClassNOIOE(fileName)) .filter((Class c) -> TestingClass.class.isAssignableFrom(c)) .collect(ReadOnlyArrayList.streamCollector()); textBar.print(bp); for (final Class c : testClasses) { final String cName = c.getCanonicalName(); System.out.println(BRED + " Testing Class: " + RESET + BGREEN + cName + RESET); final TestingClass tc; try { tc = (TestingClass) c.getDeclaredConstructor().newInstance(); } catch (Exception | Error e) { System.out.println( EXCC.toString(e) + '\n' + e.getMessage() + "\n\n" + "For TestingClass:\n " + BYELLOW + cName + RESET + '\n' + "Unable to invoke the Zero-Argument Constructor for that Class.\n" + "Did you remember to declare a public, Zero-Argument Constructor?\n" ); System.exit(0); /* shhh compiler !! */ throw null; } final boolean testResults = tc.runTests(log); final String logFileName = logSubDirName + cName + ".txt"; final String htmlFileName = logSubDirName + cName + ".html"; final String testOutput = log.getString(); final String htmlOutput = C.toHTML(testOutput, true, true, cName); log.erase(); System.out.println(" Writing File: " + BYELLOW + logFileName + RESET); FileRW.writeFile(testOutput, logFileName); System.out.println(" Writing File: " + BYELLOW + htmlFileName + RESET); FileRW.writeFile(htmlOutput, htmlFileName); if (! testResults) { System.out.println( testOutput + '\n' + BRED + "*** FAILED ***" + RESET ); System.exit(-1); } } } System.out.println(); } // ******************************************************************************************** // ******************************************************************************************** // Helper Method // ******************************************************************************************** // ******************************************************************************************** // All this does is filter out any packages which do not have a '../test-files/' subd-directory // The only reason this is necessary at all is somewhat because I have the capacity to get a // little over-zealous with the "Read-Only" thing. Otherewise, this would be several lines // shorter... private static ReadOnlyList<BuildPackage> mustHaveTestFilesDir (final ReadOnlyList<BuildPackage> allPackages) { ROArrayListBuilder<BuildPackage> roalb = new ROArrayListBuilder<>(); for (BuildPackage bp : allPackages) { final File testFilesDir = new File(bp.pkgRootDirectory + "test-files" + File.separator); if ((! testFilesDir.exists()) || (! testFilesDir.isDirectory())) continue; roalb.add(bp); } return roalb.build(); } } |