1 /* 2 * Copyright (c) 2013, 2014, 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 /* 26 * @test 27 * @summary Test all aspects of sjavac. 28 * @bug 8004658 8042441 8042699 8054461 29 * 30 * @build Wrapper 31 * @run main Wrapper SJavac 32 */ 33 34 import java.util.*; 35 import java.io.*; 36 import java.nio.file.*; 37 import java.nio.file.attribute.*; 38 import java.nio.charset.*; 39 40 import com.sun.tools.sjavac.Main; 41 42 public class SJavac { 43 44 public static void main(String... args) throws Exception { 45 try { 46 SJavac s = new SJavac(); 47 s.test(); 48 } finally { 49 System.out.println("\ntest complete\n"); 50 } 51 } 52 53 FileSystem defaultfs = FileSystems.getDefault(); 54 String serverArg = "--server:" 55 + "portfile=testportfile," 56 + "background=false"; 57 58 // Where to put generated sources that will 59 // test aspects of sjavac, ie JTWork/scratch/gensrc 60 Path gensrc; 61 // More gensrc dirs are used to test merging of serveral source roots. 62 Path gensrc2; 63 Path gensrc3; 64 65 // Where to put compiled classes. 66 Path bin; 67 // Where to put c-header files. 68 Path headers; 69 70 // The sjavac compiler. 71 Main main = new Main(); 72 73 // Remember the previous bin and headers state here. 74 Map<String,Long> previous_bin_state; 75 Map<String,Long> previous_headers_state; 76 77 public void test() throws Exception { 78 gensrc = defaultfs.getPath("gensrc"); 79 gensrc2 = defaultfs.getPath("gensrc2"); 80 gensrc3 = defaultfs.getPath("gensrc3"); 81 bin = defaultfs.getPath("bin"); 82 headers = defaultfs.getPath("headers"); 83 84 Files.createDirectory(gensrc); 85 Files.createDirectory(gensrc2); 86 Files.createDirectory(gensrc3); 87 Files.createDirectory(bin); 88 Files.createDirectory(headers); 89 90 initialCompile(); 91 incrementalCompileNoChanges(); 92 incrementalCompileDroppingClasses(); 93 incrementalCompileWithChange(); 94 incrementalCompileDropAllNatives(); 95 incrementalCompileAddNative(); 96 incrementalCompileChangeNative(); 97 compileWithOverrideSource(); 98 compileWithInvisibleSources(); 99 compileCircularSources(); 100 compileExcludingDependency(); 101 incrementalCompileTestFullyQualifiedRef(); 102 compileWithAtFile(); 103 104 delete(gensrc); 105 delete(gensrc2); 106 delete(gensrc3); 107 delete(bin); 108 delete(headers); 109 } 110 111 void initialCompile() throws Exception { 112 System.out.println("\nInitial compile of gensrc."); 113 System.out.println("----------------------------"); 114 populate(gensrc, 115 "alfa/omega/AINT.java", 116 "package alfa.omega; public interface AINT { void aint(); }", 117 118 "alfa/omega/A.java", 119 "package alfa.omega; public class A implements AINT { "+ 120 "public final static int DEFINITION = 17; public void aint() { } }", 121 122 "alfa/omega/AA.java", 123 "package alfa.omega;"+ 124 "// A package private class, not contributing to the public api.\n"+ 125 "class AA {"+ 126 " // A properly nested static inner class.\n"+ 127 " static class AAA { }\n"+ 128 " // A properly nested inner class.\n"+ 129 " class AAAA { }\n"+ 130 " Runnable foo() {\n"+ 131 " // A proper anonymous class.\n"+ 132 " return new Runnable() { public void run() { } };\n"+ 133 " }\n"+ 134 " AAA aaa;\n"+ 135 " AAAA aaaa;\n"+ 136 " AAAAA aaaaa;\n"+ 137 "}\n"+ 138 "class AAAAA {\n"+ 139 " // A bad auxiliary class, but no one is referencing it\n"+ 140 " // from outside of this source file, therefore it is ok.\n"+ 141 "}\n", 142 143 "beta/BINT.java", 144 "package beta;public interface BINT { void foo(); }", 145 146 "beta/B.java", 147 "package beta; import alfa.omega.A; public class B {"+ 148 "private int b() { return A.DEFINITION; } native void foo(); }"); 149 150 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1", 151 serverArg, "--log=debug"); 152 previous_bin_state = collectState(bin); 153 previous_headers_state = collectState(headers); 154 } 155 156 void incrementalCompileNoChanges() throws Exception { 157 System.out.println("\nTesting that no change in sources implies no change in binaries."); 158 System.out.println("------------------------------------------------------------------"); 159 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1", 160 serverArg, "--log=debug"); 161 Map<String,Long> new_bin_state = collectState(bin); 162 verifyEqual(new_bin_state, previous_bin_state); 163 Map<String,Long> new_headers_state = collectState(headers); 164 verifyEqual(previous_headers_state, new_headers_state); 165 } 166 167 void incrementalCompileDroppingClasses() throws Exception { 168 System.out.println("\nTesting that deleting AA.java deletes all"); 169 System.out.println("generated inner class as well as AA.class"); 170 System.out.println("-----------------------------------------"); 171 removeFrom(gensrc, "alfa/omega/AA.java"); 172 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1", 173 serverArg, "--log=debug"); 174 Map<String,Long> new_bin_state = collectState(bin); 175 verifyThatFilesHaveBeenRemoved(previous_bin_state, new_bin_state, 176 "bin/alfa/omega/AA$1.class", 177 "bin/alfa/omega/AA$AAAA.class", 178 "bin/alfa/omega/AA$AAA.class", 179 "bin/alfa/omega/AAAAA.class", 180 "bin/alfa/omega/AA.class"); 181 182 previous_bin_state = new_bin_state; 183 Map<String,Long> new_headers_state = collectState(headers); 184 verifyEqual(previous_headers_state, new_headers_state); 185 } 186 187 void incrementalCompileWithChange() throws Exception { 188 System.out.println("\nNow update the A.java file with a new timestamps and"); 189 System.out.println("new final static definition. This should trigger a recompile,"); 190 System.out.println("not only of alfa, but also beta."); 191 System.out.println("But check that the generated native header was not updated!"); 192 System.out.println("Since we did not modify the native api of B."); 193 System.out.println("-------------------------------------------------------------"); 194 195 populate(gensrc,"alfa/omega/A.java", 196 "package alfa.omega; public class A implements AINT { "+ 197 "public final static int DEFINITION = 18; public void aint() { } private void foo() { } }"); 198 199 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1", 200 serverArg, "--log=debug"); 201 Map<String,Long> new_bin_state = collectState(bin); 202 203 verifyNewerFiles(previous_bin_state, new_bin_state, 204 "bin/alfa/omega/A.class", 205 "bin/alfa/omega/AINT.class", 206 "bin/beta/B.class", 207 "bin/beta/BINT.class", 208 "bin/javac_state"); 209 previous_bin_state = new_bin_state; 210 211 Map<String,Long> new_headers_state = collectState(headers); 212 verifyEqual(new_headers_state, previous_headers_state); 213 } 214 215 void incrementalCompileDropAllNatives() throws Exception { 216 System.out.println("\nNow update the B.java file with one less native method,"); 217 System.out.println("ie it has no longer any methods!"); 218 System.out.println("Verify that beta_B.h is removed!"); 219 System.out.println("---------------------------------------------------------"); 220 221 populate(gensrc,"beta/B.java", 222 "package beta; import alfa.omega.A; public class B {"+ 223 "private int b() { return A.DEFINITION; } }"); 224 225 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1", 226 serverArg, "--log=debug"); 227 Map<String,Long> new_bin_state = collectState(bin); 228 verifyNewerFiles(previous_bin_state, new_bin_state, 229 "bin/beta/B.class", 230 "bin/beta/BINT.class", 231 "bin/javac_state"); 232 previous_bin_state = new_bin_state; 233 234 Map<String,Long> new_headers_state = collectState(headers); 235 verifyThatFilesHaveBeenRemoved(previous_headers_state, new_headers_state, 236 "headers/beta_B.h"); 237 previous_headers_state = new_headers_state; 238 } 239 240 void incrementalCompileAddNative() throws Exception { 241 System.out.println("\nNow update the B.java file with a final static annotated with @Native."); 242 System.out.println("Verify that beta_B.h is added again!"); 243 System.out.println("------------------------------------------------------------------------"); 244 245 populate(gensrc,"beta/B.java", 246 "package beta; import alfa.omega.A; public class B {"+ 247 "private int b() { return A.DEFINITION; } "+ 248 "@java.lang.annotation.Native final static int alfa = 42; }"); 249 250 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1", 251 serverArg, "--log=debug"); 252 Map<String,Long> new_bin_state = collectState(bin); 253 verifyNewerFiles(previous_bin_state, new_bin_state, 254 "bin/beta/B.class", 255 "bin/beta/BINT.class", 256 "bin/javac_state"); 257 previous_bin_state = new_bin_state; 258 259 Map<String,Long> new_headers_state = collectState(headers); 260 verifyThatFilesHaveBeenAdded(previous_headers_state, new_headers_state, 261 "headers/beta_B.h"); 262 previous_headers_state = new_headers_state; 263 } 264 265 void incrementalCompileChangeNative() throws Exception { 266 System.out.println("\nNow update the B.java file with a new value for the final static"+ 267 " annotated with @Native."); 268 System.out.println("Verify that beta_B.h is rewritten again!"); 269 System.out.println("-------------------------------------------------------------------"); 270 271 populate(gensrc,"beta/B.java", 272 "package beta; import alfa.omega.A; public class B {"+ 273 "private int b() { return A.DEFINITION; } "+ 274 "@java.lang.annotation.Native final static int alfa = 43; }"); 275 276 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1", 277 serverArg, "--log=debug"); 278 Map<String,Long> new_bin_state = collectState(bin); 279 verifyNewerFiles(previous_bin_state, new_bin_state, 280 "bin/beta/B.class", 281 "bin/beta/BINT.class", 282 "bin/javac_state"); 283 previous_bin_state = new_bin_state; 284 285 Map<String,Long> new_headers_state = collectState(headers); 286 verifyNewerFiles(previous_headers_state, new_headers_state, 287 "headers/beta_B.h"); 288 previous_headers_state = new_headers_state; 289 } 290 291 void compileWithOverrideSource() throws Exception { 292 System.out.println("\nNow verify that we can override sources to be compiled."); 293 System.out.println("Compile gensrc and gensrc2. However do not compile broken beta.B in gensrc,"); 294 System.out.println("only compile ok beta.B in gensrc2."); 295 System.out.println("---------------------------------------------------------------------------"); 296 297 delete(gensrc); 298 delete(gensrc2); 299 delete(bin); 300 previous_bin_state = collectState(bin); 301 302 populate(gensrc,"alfa/omega/A.java", 303 "package alfa.omega; import beta.B; import gamma.C; public class A { B b; C c; }", 304 "beta/B.java", 305 "package beta; public class B { broken", 306 "gamma/C.java", 307 "package gamma; public class C { }"); 308 309 populate(gensrc2, 310 "beta/B.java", 311 "package beta; public class B { }"); 312 313 compile("-x", "beta", "gensrc", "gensrc2", "-d", "bin", "-h", "headers", "-j", "1", 314 serverArg); 315 Map<String,Long> new_bin_state = collectState(bin); 316 verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state, 317 "bin/alfa/omega/A.class", 318 "bin/beta/B.class", 319 "bin/gamma/C.class", 320 "bin/javac_state"); 321 322 System.out.println("----- Compile with exluded beta went well!"); 323 delete(bin); 324 compileExpectFailure("gensrc", "gensrc2", "-d", "bin", "-h", "headers", "-j", "1", 325 serverArg); 326 327 System.out.println("----- Compile without exluded beta failed, as expected! Good!"); 328 delete(bin); 329 } 330 331 void compileWithInvisibleSources() throws Exception { 332 System.out.println("\nNow verify that we can make sources invisible to linking (sourcepath)."); 333 System.out.println("Compile gensrc and link against gensrc2 and gensrc3, however"); 334 System.out.println("gensrc2 contains broken code in beta.B, thus we must exclude that package"); 335 System.out.println("fortunately gensrc3 contains a proper beta.B."); 336 System.out.println("------------------------------------------------------------------------"); 337 338 // Start with a fresh gensrcs and bin. 339 delete(gensrc); 340 delete(gensrc2); 341 delete(gensrc3); 342 delete(bin); 343 previous_bin_state = collectState(bin); 344 345 populate(gensrc,"alfa/omega/A.java", 346 "package alfa.omega; import beta.B; import gamma.C; public class A { B b; C c; }"); 347 populate(gensrc2,"beta/B.java", 348 "package beta; public class B { broken", 349 "gamma/C.java", 350 "package gamma; public class C { }"); 351 populate(gensrc3, "beta/B.java", 352 "package beta; public class B { }"); 353 354 compile("gensrc", "-x", "beta", "-sourcepath", "gensrc2", 355 "-sourcepath", "gensrc3", "-d", "bin", "-h", "headers", "-j", "1", 356 serverArg); 357 358 System.out.println("The first compile went well!"); 359 Map<String,Long> new_bin_state = collectState(bin); 360 verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state, 361 "bin/alfa/omega/A.class", 362 "bin/javac_state"); 363 364 System.out.println("----- Compile with exluded beta went well!"); 365 delete(bin); 366 compileExpectFailure("gensrc", "-sourcepath", "gensrc2", "-sourcepath", "gensrc3", 367 "-d", "bin", "-h", "headers", "-j", "1", 368 serverArg); 369 370 System.out.println("----- Compile without exluded beta failed, as expected! Good!"); 371 delete(bin); 372 } 373 374 void compileCircularSources() throws Exception { 375 System.out.println("\nNow verify that circular sources split on multiple cores can be compiled."); 376 System.out.println("---------------------------------------------------------------------------"); 377 378 // Start with a fresh gensrcs and bin. 379 delete(gensrc); 380 delete(gensrc2); 381 delete(gensrc3); 382 delete(bin); 383 previous_bin_state = collectState(bin); 384 385 populate(gensrc,"alfa/omega/A.java", 386 "package alfa.omega; public class A { beta.B b; }", 387 "beta/B.java", 388 "package beta; public class B { gamma.C c; }", 389 "gamma/C.java", 390 "package gamma; public class C { alfa.omega.A a; }"); 391 392 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "3", 393 serverArg,"--log=debug"); 394 Map<String,Long> new_bin_state = collectState(bin); 395 verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state, 396 "bin/alfa/omega/A.class", 397 "bin/beta/B.class", 398 "bin/gamma/C.class", 399 "bin/javac_state"); 400 delete(bin); 401 } 402 403 /** 404 * Tests compiling class A that depends on class B without compiling class B 405 * @throws Exception If test fails 406 */ 407 void compileExcludingDependency() throws Exception { 408 System.out.println("\nVerify that excluding classes from compilation but not from linking works."); 409 System.out.println("---------------------------------------------------------------------------"); 410 411 delete(gensrc); 412 delete(bin); 413 previous_bin_state = collectState(bin); 414 415 populate(gensrc, 416 "alfa/omega/A.java", 417 "package alfa.omega; public class A { beta.B b; }", 418 "beta/B.java", 419 "package beta; public class B { }"); 420 421 compile("-x", "beta", "-src", "gensrc", "-x", "alfa/omega", "-sourcepath", "gensrc", 422 "-d", "bin", serverArg); 423 424 Map<String,Long> new_bin_state = collectState(bin); 425 verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state, 426 "bin/alfa/omega/A.class", 427 "bin/javac_state"); 428 } 429 430 void incrementalCompileTestFullyQualifiedRef() throws Exception { 431 System.out.println("\nVerify that \"alfa.omega.A a;\" does create a proper dependency."); 432 System.out.println("----------------------------------------------------------------"); 433 434 populate(gensrc, 435 "alfa/omega/A.java", 436 "package alfa.omega; public class A { "+ 437 " public final static int DEFINITION = 18; "+ 438 " public void hello() { }"+ 439 "}", 440 "beta/B.java", 441 "package beta; public class B { "+ 442 " public void world() { alfa.omega.A a; }"+ 443 "}"); 444 445 compile("gensrc", "-d", "bin", "-j", "1", 446 serverArg, "--log=debug"); 447 Map<String,Long> previous_bin_state = collectState(bin); 448 449 // Change pubapi of A, this should trigger a recompile of B. 450 populate(gensrc, 451 "alfa/omega/A.java", 452 "package alfa.omega; public class A { "+ 453 " public final static int DEFINITION = 19; "+ 454 " public void hello() { }"+ 455 "}"); 456 457 compile("gensrc", "-d", "bin", "-j", "1", 458 serverArg, "--log=debug"); 459 Map<String,Long> new_bin_state = collectState(bin); 460 461 verifyNewerFiles(previous_bin_state, new_bin_state, 462 "bin/alfa/omega/A.class", 463 "bin/beta/B.class", 464 "bin/javac_state"); 465 } 466 467 /** 468 * Tests @atfile 469 * @throws Exception If test fails 470 */ 471 void compileWithAtFile() throws Exception { 472 System.out.println("\nTest @atfile with command line content."); 473 System.out.println("---------------------------------------"); 474 475 delete(gensrc); 476 delete(gensrc2); 477 delete(bin); 478 479 populate(gensrc, 480 "list.txt", 481 "-if */alfa/omega/A.java\n-if */beta/B.java\ngensrc\n-d bin\n", 482 "alfa/omega/A.java", 483 "package alfa.omega; import beta.B; public class A { B b; }", 484 "beta/B.java", 485 "package beta; public class B { }", 486 "beta/C.java", 487 "broken"); 488 previous_bin_state = collectState(bin); 489 compile("@gensrc/list.txt", "--server:portfile=testserver,background=false"); 490 491 Map<String,Long> new_bin_state = collectState(bin); 492 verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state, 493 "bin/javac_state", 494 "bin/alfa/omega/A.class", 495 "bin/beta/B.class"); 496 } 497 498 void removeFrom(Path dir, String... args) throws IOException { 499 for (String filename : args) { 500 Path p = dir.resolve(filename); 501 Files.delete(p); 502 } 503 } 504 505 void populate(Path src, String... args) throws IOException { 506 if (!Files.exists(src)) { 507 Files.createDirectory(src); 508 } 509 String[] a = args; 510 for (int i = 0; i<a.length; i+=2) { 511 String filename = a[i]; 512 String content = a[i+1]; 513 Path p = src.resolve(filename); 514 Files.createDirectories(p.getParent()); 515 PrintWriter out = new PrintWriter(Files.newBufferedWriter(p, 516 Charset.defaultCharset())); 517 out.println(content); 518 out.close(); 519 } 520 } 521 522 void delete(final Path root) throws IOException { 523 if (!Files.exists(root)) return; 524 Files.walkFileTree(root, new SimpleFileVisitor<Path>() { 525 @Override 526 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException 527 { 528 Files.delete(file); 529 return FileVisitResult.CONTINUE; 530 } 531 532 @Override 533 public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException 534 { 535 if (e == null) { 536 if (!dir.equals(root)) Files.delete(dir); 537 return FileVisitResult.CONTINUE; 538 } else { 539 // directory iteration failed 540 throw e; 541 } 542 } 543 }); 544 } 545 546 void compile(String... args) throws Exception { 547 int rc = main.go(args, System.out, System.err); 548 if (rc != 0) throw new Exception("Error during compile!"); 549 550 // Wait a second, to get around the (temporary) problem with 551 // second resolution in the Java file api. But do not do this 552 // on windows where the timestamps work. 553 long in_a_sec = System.currentTimeMillis()+1000; 554 while (in_a_sec > System.currentTimeMillis()) { 555 try { 556 Thread.sleep(1000); 557 } catch (InterruptedException e) { 558 } 559 } 560 } 561 562 void compileExpectFailure(String... args) throws Exception { 563 int rc = main.go(args, System.out, System.err); 564 if (rc == 0) throw new Exception("Expected error during compile! Did not fail!"); 565 } 566 567 Map<String,Long> collectState(Path dir) throws IOException { 568 final Map<String,Long> files = new HashMap<>(); 569 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { 570 @Override 571 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 572 throws IOException 573 { 574 files.put(file.toString(),new Long(Files.getLastModifiedTime(file).toMillis())); 575 return FileVisitResult.CONTINUE; 576 } 577 }); 578 return files; 579 } 580 581 void verifyThatFilesHaveBeenRemoved(Map<String,Long> from, 582 Map<String,Long> to, 583 String... args) throws Exception { 584 585 Set<String> froms = from.keySet(); 586 Set<String> tos = to.keySet(); 587 588 if (froms.equals(tos)) { 589 throw new Exception("Expected new state to have fewer files than previous state!"); 590 } 591 592 for (String t : tos) { 593 if (!froms.contains(t)) { 594 throw new Exception("Expected "+t+" to exist in previous state!"); 595 } 596 } 597 598 for (String f : args) { 599 f = f.replace("/", File.separator); 600 if (!froms.contains(f)) { 601 throw new Exception("Expected "+f+" to exist in previous state!"); 602 } 603 if (tos.contains(f)) { 604 throw new Exception("Expected "+f+" to have been removed from the new state!"); 605 } 606 } 607 608 if (froms.size() - args.length != tos.size()) { 609 throw new Exception("There are more removed files than the expected list!"); 610 } 611 } 612 613 void verifyThatFilesHaveBeenAdded(Map<String,Long> from, 614 Map<String,Long> to, 615 String... args) throws Exception { 616 617 Set<String> froms = from.keySet(); 618 Set<String> tos = to.keySet(); 619 620 if (froms.equals(tos)) { 621 throw new Exception("Expected new state to have more files than previous state!"); 622 } 623 624 for (String t : froms) { 625 if (!tos.contains(t)) { 626 throw new Exception("Expected "+t+" to exist in new state!"); 627 } 628 } 629 630 for (String f : args) { 631 f = f.replace("/", File.separator); 632 if (!tos.contains(f)) { 633 throw new Exception("Expected "+f+" to have been added to new state!"); 634 } 635 if (froms.contains(f)) { 636 throw new Exception("Expected "+f+" to not exist in previous state!"); 637 } 638 } 639 640 if (froms.size() + args.length != tos.size()) { 641 throw new Exception("There are more added files than the expected list!"); 642 } 643 } 644 645 void verifyNewerFiles(Map<String,Long> from, 646 Map<String,Long> to, 647 String... args) throws Exception { 648 if (!from.keySet().equals(to.keySet())) { 649 throw new Exception("Expected the set of files to be identical!"); 650 } 651 Set<String> files = new HashSet<String>(); 652 for (String s : args) { 653 files.add(s.replace("/", File.separator)); 654 } 655 for (String fn : from.keySet()) { 656 long f = from.get(fn); 657 long t = to.get(fn); 658 if (files.contains(fn)) { 659 if (t <= f) { 660 throw new Exception("Expected "+fn+" to have a more recent timestamp!"); 661 } 662 } else { 663 if (t != f) { 664 throw new Exception("Expected "+fn+" to have the same timestamp!"); 665 } 666 } 667 } 668 } 669 670 String print(Map<String,Long> m) { 671 StringBuilder b = new StringBuilder(); 672 Set<String> keys = m.keySet(); 673 for (String k : keys) { 674 b.append(k+" "+m.get(k)+"\n"); 675 } 676 return b.toString(); 677 } 678 679 void verifyEqual(Map<String,Long> from, Map<String,Long> to) throws Exception { 680 if (!from.equals(to)) { 681 System.out.println("FROM---"+print(from)); 682 System.out.println("TO-----"+print(to)); 683 throw new Exception("The dir should not differ! But it does!"); 684 } 685 } 686 }