XPreferTest.java revision 3294:9adfb22ff08f
1/* 2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* 25 * @test 26 * @summary Tests which path is used to represent an implicit type given 27 * various xprefer arguments and multiple .class / .java files involved. 28 * @bug 8028196 29 * @modules jdk.compiler 30 */ 31 32import java.io.File; 33import java.io.FileWriter; 34import java.io.IOException; 35import java.io.PrintWriter; 36import java.io.StringWriter; 37import java.util.ArrayList; 38import java.util.Arrays; 39import java.util.Collection; 40import java.util.Iterator; 41import java.util.List; 42import java.util.ListIterator; 43import java.util.NoSuchElementException; 44import java.util.Scanner; 45import java.util.regex.Pattern; 46 47import javax.tools.JavaCompiler; 48import javax.tools.JavaCompiler.CompilationTask; 49import javax.tools.StandardJavaFileManager; 50import javax.tools.ToolProvider; 51 52 53public class XPreferTest { 54 55 enum Dir { 56 SOURCE_PATH("src"), 57 CLASS_PATH("cp"), 58 BOOT_PATH("boot"); 59 60 File file; 61 Dir(String dir) { 62 this.file = new File(dir); 63 } 64 } 65 66 enum ImplicitOption { 67 XPREFER_SOURCE("-Xprefer:source"), 68 XPREFER_NEWER("-Xprefer:newer"), 69 XXUSERPATHSFIRST("-XXuserPathsFirst"); 70 71 final String optionString; 72 private ImplicitOption(String optionString) { 73 this.optionString = optionString; 74 } 75 } 76 77 final static JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); 78 final static StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); 79 final static File OUTPUT_DIR = new File("out"); 80 81 public static void main(String... args) throws Exception { 82 try { 83 // Initialize test-directories 84 OUTPUT_DIR.mkdir(); 85 for (Dir dir : Dir.values()) 86 dir.file.mkdir(); 87 88 int testCaseCounter = 0; 89 90 for (List<Dir> dirSubset : SubseqIter.subseqsOf(Dir.values())) { 91 92 if (dirSubset.isEmpty()) 93 continue; 94 95 for (ImplicitOption policy : ImplicitOption.values()) { 96 for (List<Dir> dirOrder : PermutationIterator.permutationsOf(dirSubset)) { 97 new TestCase(dirOrder, policy, testCaseCounter++).run(); 98 } 99 } 100 } 101 } finally { 102 fm.close(); 103 } 104 } 105 106 static class TestCase { 107 108 String classId; 109 List<Dir> dirs; 110 ImplicitOption option; 111 112 public TestCase(List<Dir> dirs, ImplicitOption option, int testCaseNum) { 113 this.dirs = dirs; 114 this.option = option; 115 this.classId = "XPreferTestImplicit" + testCaseNum; 116 } 117 118 void run() throws Exception { 119 120 System.out.println("Test:"); 121 System.out.println(" Class id: " + classId); 122 System.out.println(" Dirs: " + dirs); 123 System.out.println(" Option: " + option); 124 125 createTestFiles(); 126 String compileOutput = compile(); 127 Dir actual = getChosenOrigin(compileOutput); 128 Dir expected = getExpectedOrigin(); 129 130 System.out.println(" Expected: " + expected); 131 System.out.println(" Actual: " + actual); 132 133 if (actual != expected) { 134 throw new RuntimeException(String.format( 135 "Expected javac to choose %s but %s was chosen", 136 expected == null ? "<none>" : expected.name(), 137 actual == null ? "<none>" : actual.name())); 138 } 139 } 140 141 Dir getExpectedOrigin() { 142 143 Dir newest = dirs.get(0); 144 145 switch (option) { 146 147 case XPREFER_NEWER: 148 149 Dir cls = dirs.contains(Dir.BOOT_PATH) ? Dir.BOOT_PATH 150 : dirs.contains(Dir.CLASS_PATH) ? Dir.CLASS_PATH 151 : null; 152 153 Dir src = dirs.contains(Dir.SOURCE_PATH) ? Dir.SOURCE_PATH 154 : null; 155 156 for (Dir dir : dirs) 157 if (dir == cls || dir == src) 158 return dir; 159 160 return null; 161 162 case XPREFER_SOURCE: 163 return dirs.contains(Dir.SOURCE_PATH) ? Dir.SOURCE_PATH 164 : dirs.contains(Dir.BOOT_PATH) ? Dir.BOOT_PATH 165 : dirs.contains(Dir.CLASS_PATH) ? Dir.CLASS_PATH 166 : null; 167 168 case XXUSERPATHSFIRST: 169 170 for (Dir dir : dirs) 171 if (dir == Dir.SOURCE_PATH || dir == Dir.CLASS_PATH) 172 return dir; 173 174 // Neither SOURCE_PATH nor CLASS_PATH among dirs. Safty check: 175 if (newest != Dir.BOOT_PATH) 176 throw new AssertionError("Expected to find BOOT_PATH"); 177 178 return Dir.BOOT_PATH; 179 180 default: 181 throw new RuntimeException("Unhandled policy case."); 182 } 183 } 184 185 Dir getChosenOrigin(String compilerOutput) { 186 Scanner s = new Scanner(compilerOutput); 187 while (s.hasNextLine()) { 188 String line = s.nextLine(); 189 if (line.matches("\\[loading .*\\]")) { 190 for (Dir dir : Dir.values()) { 191 // On Windows all paths are printed with '/' except 192 // paths inside zip-files, which are printed with '\'. 193 // For this reason we accept both '/' and '\' below. 194 String regex = dir.file.getName() + "[\\\\/]" + classId; 195 if (Pattern.compile(regex).matcher(line).find()) 196 return dir; 197 } 198 } 199 } 200 return null; 201 } 202 203 String compile() throws IOException { 204 205 // Create a class that references classId 206 File explicit = new File("ExplicitClass.java"); 207 FileWriter filewriter = new FileWriter(explicit); 208 filewriter.append("class ExplicitClass { " + classId + " implicit; }"); 209 filewriter.close(); 210 211 StringWriter sw = new StringWriter(); 212 213 com.sun.tools.javac.Main.compile(new String[] { 214 "-verbose", 215 option.optionString, 216 "-source", "8", "-target", "8", 217 "-sourcepath", Dir.SOURCE_PATH.file.getPath(), 218 "-classpath", Dir.CLASS_PATH.file.getPath(), 219 "-Xbootclasspath/p:" + Dir.BOOT_PATH.file.getPath(), 220 "-d", XPreferTest.OUTPUT_DIR.getPath(), 221 explicit.getPath() 222 }, new PrintWriter(sw)); 223 224 return sw.toString(); 225 } 226 227 void createTestFiles() throws IOException { 228 long t = 1390927988755L; // Tue Jan 28 17:53:08 CET 2014 229 for (Dir dir : dirs) { 230 createFile(dir).setLastModified(t); 231 t -= 10000; 232 } 233 } 234 235 File createFile(Dir dir) throws IOException { 236 File src = new File(dir.file, classId + ".java"); 237 try (FileWriter w = new FileWriter(src)) { 238 w.append("public class " + classId + " {}"); 239 } 240 // If we're after the ".java" representation, we're done... 241 if(dir == Dir.SOURCE_PATH) 242 return src; 243 // ...otherwise compile into a ".class". 244 CompilationTask task = comp.getTask(null, fm, null, null, null, 245 fm.getJavaFileObjects(src)); 246 File dest = new File(dir.file, classId + ".class"); 247 if(!task.call() || !dest.exists()) 248 throw new RuntimeException("Compilation failure."); 249 src.delete(); 250 return dest; 251 } 252 } 253} 254 255// Iterator for iteration over all subsequences of a given list. 256class SubseqIter<T> implements Iterator<List<T>> { 257 258 List<T> elements; 259 boolean[] states; 260 261 public SubseqIter(Collection<T> c) { 262 states = new boolean[c.size()]; 263 elements = new ArrayList<T>(c); 264 } 265 266 public static <T> Iterable<List<T>> subseqsOf(final T[] t) { 267 return new Iterable<List<T>>() { 268 @Override 269 public Iterator<List<T>> iterator() { 270 return new SubseqIter<T>(Arrays.asList(t)); 271 } 272 }; 273 } 274 275 // Roll values in 'states' from index i and forward. 276 // Return true if we wrapped back to zero. 277 private boolean roll(int i) { 278 if (i == states.length) 279 return true; 280 if (!roll(i + 1)) 281 return false; 282 states[i] = !states[i]; 283 return !states[i]; 284 } 285 286 @Override 287 public List<T> next() { 288 if (!hasNext()) 289 throw new NoSuchElementException(); 290 // Include element i if states[i] is true 291 List<T> next = new ArrayList<T>(); 292 for (int i = 0; i < states.length; i++) 293 if (states[i]) 294 next.add(elements.get(i)); 295 if (roll(0)) 296 states = null; // hasNext() == false from now on. 297 return next; 298 } 299 300 @Override 301 public boolean hasNext() { 302 return states != null; 303 } 304 305 @Override 306 public void remove() { 307 throw new UnsupportedOperationException(); 308 } 309} 310 311class PermutationIterator<T> implements Iterator<List<T>> { 312 313 DirInt head; 314 boolean hasNext = true; 315 316 public PermutationIterator(List<T> toPermute) { 317 ListIterator<T> iter = toPermute.listIterator(); 318 if (iter.hasNext()) 319 head = new DirInt(iter.nextIndex(), iter.next()); 320 DirInt prev = head; 321 while (iter.hasNext()) { 322 DirInt di = new DirInt(iter.nextIndex(), iter.next()); 323 di.left = prev; 324 prev.right = di; 325 prev = di; 326 } 327 } 328 329 public static <T> Iterable<List<T>> permutationsOf(final List<T> list) { 330 return new Iterable<List<T>>() { 331 public Iterator<List<T>> iterator() { 332 return new PermutationIterator<>(list); 333 } 334 }; 335 } 336 337 @Override 338 public boolean hasNext() { 339 return hasNext; 340 } 341 342 @Override 343 public List<T> next() { 344 // Prep return value based on current state 345 List<T> result = new ArrayList<>(); 346 for (DirInt di = head; di != null; di = di.right) 347 result.add(di.object); 348 349 // Step state forward 350 DirInt maxMob = null; 351 for (DirInt di = head; di != null; di = di.right) 352 if (di.isMobile() && (maxMob == null || di.val > maxMob.val)) 353 maxMob = di; 354 355 if (maxMob == null) { 356 hasNext = false; 357 } else { 358 maxMob.swapWithAdjacent(); 359 for (DirInt di = head; di != null; di = di.right) 360 if (di.val > maxMob.val) 361 di.facingLeft = !di.facingLeft; 362 } 363 return result; 364 } 365 366 private final class DirInt { 367 int val; 368 T object; 369 DirInt left, right; 370 boolean facingLeft = true; 371 372 public DirInt(int val, T object) { 373 this.val = val; 374 this.object = object; 375 } 376 377 boolean isMobile() { 378 DirInt adjacent = facingLeft ? left : right; 379 return adjacent != null && val > adjacent.val; 380 } 381 382 public void swapWithAdjacent() { 383 DirInt l = facingLeft ? left : this; 384 DirInt r = facingLeft ? this : right; 385 if (head == l) head = r; 386 if (l.left != null) l.left.right = r; 387 if (r.right != null) r.right.left = l; 388 l.right = r.right; 389 r.left = l.left; 390 r.right = l; 391 l.left = r; 392 } 393 } 394 395 @Override 396 public void remove() { 397 throw new UnsupportedOperationException(); 398 } 399} 400