1 /* 2 * Copyright 2016 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.turbine.lower; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH; 21 import static java.nio.charset.StandardCharsets.UTF_8; 22 import static org.junit.Assert.fail; 23 24 import com.google.common.base.Joiner; 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.collect.ImmutableMap; 27 import com.google.common.io.ByteStreams; 28 import com.google.turbine.binder.Binder; 29 import com.google.turbine.binder.Binder.BindingResult; 30 import com.google.turbine.binder.ClassPathBinder; 31 import com.google.turbine.binder.bound.SourceTypeBoundClass; 32 import com.google.turbine.binder.env.SimpleEnv; 33 import com.google.turbine.binder.sym.ClassSymbol; 34 import com.google.turbine.binder.sym.FieldSymbol; 35 import com.google.turbine.binder.sym.MethodSymbol; 36 import com.google.turbine.binder.sym.ParamSymbol; 37 import com.google.turbine.binder.sym.TyVarSymbol; 38 import com.google.turbine.bytecode.ByteReader; 39 import com.google.turbine.bytecode.ConstantPoolReader; 40 import com.google.turbine.diag.TurbineError; 41 import com.google.turbine.model.TurbineConstantTypeKind; 42 import com.google.turbine.model.TurbineFlag; 43 import com.google.turbine.model.TurbineTyKind; 44 import com.google.turbine.parse.Parser; 45 import com.google.turbine.testing.AsmUtils; 46 import com.google.turbine.type.Type; 47 import com.google.turbine.type.Type.ClassTy; 48 import com.google.turbine.type.Type.ClassTy.SimpleClassTy; 49 import com.google.turbine.type.Type.IntersectionTy; 50 import com.google.turbine.type.Type.PrimTy; 51 import com.google.turbine.type.Type.TyVar; 52 import java.io.IOException; 53 import java.io.OutputStream; 54 import java.nio.file.Files; 55 import java.nio.file.Path; 56 import java.util.ArrayList; 57 import java.util.LinkedHashMap; 58 import java.util.List; 59 import java.util.Map; 60 import java.util.Optional; 61 import java.util.jar.JarEntry; 62 import java.util.jar.JarOutputStream; 63 import org.junit.Rule; 64 import org.junit.Test; 65 import org.junit.rules.TemporaryFolder; 66 import org.junit.runner.RunWith; 67 import org.junit.runners.JUnit4; 68 import org.objectweb.asm.AnnotationVisitor; 69 import org.objectweb.asm.ClassReader; 70 import org.objectweb.asm.ClassVisitor; 71 import org.objectweb.asm.ClassWriter; 72 import org.objectweb.asm.FieldVisitor; 73 import org.objectweb.asm.Opcodes; 74 import org.objectweb.asm.TypePath; 75 76 @RunWith(JUnit4.class) 77 public class LowerTest { 78 79 @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); 80 81 @Test hello()82 public void hello() throws Exception { 83 84 ImmutableList<Type> interfaceTypes = 85 ImmutableList.of( 86 ClassTy.create( 87 ImmutableList.of( 88 SimpleClassTy.create( 89 new ClassSymbol("java/util/List"), 90 ImmutableList.of( 91 TyVar.create( 92 new TyVarSymbol(new ClassSymbol("test/Test"), "V"), 93 ImmutableList.of())), 94 ImmutableList.of())))); 95 Type.ClassTy xtnds = Type.ClassTy.OBJECT; 96 ImmutableMap<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tps = 97 ImmutableMap.of( 98 new TyVarSymbol(new ClassSymbol("test/Test"), "V"), 99 new SourceTypeBoundClass.TyVarInfo( 100 IntersectionTy.create( 101 ImmutableList.of( 102 ClassTy.create( 103 ImmutableList.of( 104 SimpleClassTy.create( 105 new ClassSymbol("test/Test$Inner"), 106 ImmutableList.of(), 107 ImmutableList.of()))))), 108 /* lowerBound= */ null, 109 ImmutableList.of())); 110 int access = TurbineFlag.ACC_SUPER | TurbineFlag.ACC_PUBLIC; 111 ImmutableList<SourceTypeBoundClass.MethodInfo> methods = 112 ImmutableList.of( 113 new SourceTypeBoundClass.MethodInfo( 114 new MethodSymbol(-1, new ClassSymbol("test/Test"), "f"), 115 ImmutableMap.of(), 116 PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()), 117 ImmutableList.of(), 118 ImmutableList.of(), 119 TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PUBLIC, 120 null, 121 null, 122 ImmutableList.of(), 123 null), 124 new SourceTypeBoundClass.MethodInfo( 125 new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), 126 ImmutableMap.of( 127 new TyVarSymbol(new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "V"), 128 new SourceTypeBoundClass.TyVarInfo( 129 IntersectionTy.create( 130 ImmutableList.of( 131 ClassTy.create( 132 ImmutableList.of( 133 SimpleClassTy.create( 134 new ClassSymbol("java/lang/Runnable"), 135 ImmutableList.of(), 136 ImmutableList.of()))))), 137 /* lowerBound= */ null, 138 ImmutableList.of()), 139 new TyVarSymbol(new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "E"), 140 new SourceTypeBoundClass.TyVarInfo( 141 IntersectionTy.create( 142 ImmutableList.of( 143 ClassTy.create( 144 ImmutableList.of( 145 SimpleClassTy.create( 146 new ClassSymbol("java/lang/Error"), 147 ImmutableList.of(), 148 ImmutableList.of()))))), 149 /* lowerBound= */ null, 150 ImmutableList.of())), 151 Type.VOID, 152 ImmutableList.of( 153 new SourceTypeBoundClass.ParamInfo( 154 new ParamSymbol( 155 new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "foo"), 156 PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()), 157 ImmutableList.of(), 158 0)), 159 ImmutableList.of( 160 TyVar.create( 161 new TyVarSymbol( 162 new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "E"), 163 ImmutableList.of())), 164 TurbineFlag.ACC_PUBLIC, 165 null, 166 null, 167 ImmutableList.of(), 168 null)); 169 ImmutableList<SourceTypeBoundClass.FieldInfo> fields = 170 ImmutableList.of( 171 new SourceTypeBoundClass.FieldInfo( 172 new FieldSymbol(new ClassSymbol("test/Test"), "theField"), 173 Type.ClassTy.asNonParametricClassTy(new ClassSymbol("test/Test$Inner")), 174 TurbineFlag.ACC_STATIC | TurbineFlag.ACC_FINAL | TurbineFlag.ACC_PUBLIC, 175 ImmutableList.of(), 176 null, 177 null)); 178 ClassSymbol owner = null; 179 TurbineTyKind kind = TurbineTyKind.CLASS; 180 ImmutableMap<String, ClassSymbol> children = ImmutableMap.of(); 181 ImmutableMap<String, TyVarSymbol> tyParams = 182 ImmutableMap.of("V", new TyVarSymbol(new ClassSymbol("test/Test"), "V")); 183 184 SourceTypeBoundClass c = 185 new SourceTypeBoundClass( 186 interfaceTypes, 187 xtnds, 188 tps, 189 access, 190 methods, 191 fields, 192 owner, 193 kind, 194 children, 195 tyParams, 196 null, 197 null, 198 null, 199 null, 200 ImmutableList.of(), 201 null, 202 null); 203 204 SourceTypeBoundClass i = 205 new SourceTypeBoundClass( 206 ImmutableList.of(), 207 Type.ClassTy.OBJECT, 208 ImmutableMap.of(), 209 TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PROTECTED, 210 ImmutableList.of(), 211 ImmutableList.of(), 212 new ClassSymbol("test/Test"), 213 TurbineTyKind.CLASS, 214 ImmutableMap.of("Inner", new ClassSymbol("test/Test$Inner")), 215 ImmutableMap.of(), 216 null, 217 null, 218 null, 219 null, 220 ImmutableList.of(), 221 null, 222 null); 223 224 SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> b = SimpleEnv.builder(); 225 b.put(new ClassSymbol("test/Test"), c); 226 b.put(new ClassSymbol("test/Test$Inner"), i); 227 228 Map<String, byte[]> bytes = 229 Lower.lowerAll( 230 ImmutableMap.of( 231 new ClassSymbol("test/Test"), c, new ClassSymbol("test/Test$Inner"), i), 232 ImmutableList.of(), 233 TURBINE_BOOTCLASSPATH.env()) 234 .bytes(); 235 236 assertThat(AsmUtils.textify(bytes.get("test/Test"))) 237 .isEqualTo( 238 new String( 239 ByteStreams.toByteArray( 240 LowerTest.class.getResourceAsStream("testdata/golden/outer.txt")), 241 UTF_8)); 242 assertThat(AsmUtils.textify(bytes.get("test/Test$Inner"))) 243 .isEqualTo( 244 new String( 245 ByteStreams.toByteArray( 246 LowerTest.class.getResourceAsStream("testdata/golden/inner.txt")), 247 UTF_8)); 248 } 249 250 @Test innerClassAttributeOrder()251 public void innerClassAttributeOrder() throws IOException { 252 BindingResult bound = 253 Binder.bind( 254 ImmutableList.of( 255 Parser.parse( 256 Joiner.on('\n') 257 .join( 258 "class Test {", // 259 " class Inner {", 260 " class InnerMost {}", 261 " }", 262 "}"))), 263 ClassPathBinder.bindClasspath(ImmutableList.of()), 264 TURBINE_BOOTCLASSPATH, 265 /* moduleVersion=*/ Optional.empty()); 266 Map<String, byte[]> lowered = 267 Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes(); 268 List<String> attributes = new ArrayList<>(); 269 new ClassReader(lowered.get("Test$Inner$InnerMost")) 270 .accept( 271 new ClassVisitor(Opcodes.ASM7) { 272 @Override 273 public void visitInnerClass( 274 String name, String outerName, String innerName, int access) { 275 attributes.add(String.format("%s %s %s", name, outerName, innerName)); 276 } 277 }, 278 0); 279 assertThat(attributes) 280 .containsExactly("Test$Inner Test Inner", "Test$Inner$InnerMost Test$Inner InnerMost") 281 .inOrder(); 282 } 283 284 @Test wildArrayElement()285 public void wildArrayElement() throws Exception { 286 IntegrationTestSupport.TestInput input = 287 IntegrationTestSupport.TestInput.parse( 288 new String( 289 ByteStreams.toByteArray( 290 getClass().getResourceAsStream("testdata/canon_array.test")), 291 UTF_8)); 292 293 Map<String, byte[]> actual = 294 IntegrationTestSupport.runTurbine(input.sources, ImmutableList.of()); 295 296 ByteReader reader = new ByteReader(actual.get("Test"), 0); 297 assertThat(reader.u4()).isEqualTo(0xcafebabe); // magic 298 assertThat(reader.u2()).isEqualTo(0); // minor 299 assertThat(reader.u2()).isEqualTo(52); // major 300 ConstantPoolReader pool = ConstantPoolReader.readConstantPool(reader); 301 assertThat(reader.u2()).isEqualTo(TurbineFlag.ACC_SUPER); // access 302 assertThat(pool.classInfo(reader.u2())).isEqualTo("Test"); // this 303 assertThat(pool.classInfo(reader.u2())).isEqualTo("java/lang/Object"); // super 304 assertThat(reader.u2()).isEqualTo(0); // interfaces 305 assertThat(reader.u2()).isEqualTo(1); // field count 306 assertThat(reader.u2()).isEqualTo(0); // access 307 assertThat(pool.utf8(reader.u2())).isEqualTo("i"); // name 308 assertThat(pool.utf8(reader.u2())).isEqualTo("LA$I;"); // descriptor 309 int attributesCount = reader.u2(); 310 String signature = null; 311 for (int j = 0; j < attributesCount; j++) { 312 String attributeName = pool.utf8(reader.u2()); 313 switch (attributeName) { 314 case "Signature": 315 reader.u4(); // length 316 signature = pool.utf8(reader.u2()); 317 break; 318 default: 319 reader.skip(reader.u4()); 320 break; 321 } 322 } 323 assertThat(signature).isEqualTo("LA<[*>.I;"); 324 } 325 326 @Test typePath()327 public void typePath() throws Exception { 328 BindingResult bound = 329 Binder.bind( 330 ImmutableList.of( 331 Parser.parse( 332 Joiner.on('\n') 333 .join( 334 "import java.lang.annotation.ElementType;", 335 "import java.lang.annotation.Target;", 336 "import java.util.List;", 337 "@Target({ElementType.TYPE_USE}) @interface Anno {}", 338 "class Test {", 339 " public @Anno int[][] xs;", 340 "}"))), 341 ClassPathBinder.bindClasspath(ImmutableList.of()), 342 TURBINE_BOOTCLASSPATH, 343 /* moduleVersion=*/ Optional.empty()); 344 Map<String, byte[]> lowered = 345 Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes(); 346 TypePath[] path = new TypePath[1]; 347 new ClassReader(lowered.get("Test")) 348 .accept( 349 new ClassVisitor(Opcodes.ASM7) { 350 @Override 351 public FieldVisitor visitField( 352 int access, String name, String desc, String signature, Object value) { 353 return new FieldVisitor(Opcodes.ASM7) { 354 @Override 355 public AnnotationVisitor visitTypeAnnotation( 356 int typeRef, TypePath typePath, String desc, boolean visible) { 357 path[0] = typePath; 358 return null; 359 } 360 }; 361 } 362 }, 363 0); 364 assertThat(path[0].getLength()).isEqualTo(2); 365 assertThat(path[0].getStep(0)).isEqualTo(TypePath.ARRAY_ELEMENT); 366 assertThat(path[0].getStepArgument(0)).isEqualTo(0); 367 assertThat(path[0].getStep(1)).isEqualTo(TypePath.ARRAY_ELEMENT); 368 assertThat(path[0].getStepArgument(1)).isEqualTo(0); 369 } 370 371 @Test invalidConstants()372 public void invalidConstants() throws Exception { 373 Path lib = temporaryFolder.newFile("lib.jar").toPath(); 374 try (OutputStream os = Files.newOutputStream(lib); 375 JarOutputStream jos = new JarOutputStream(os)) { 376 jos.putNextEntry(new JarEntry("Lib.class")); 377 378 ClassWriter cw = new ClassWriter(0); 379 cw.visit(52, Opcodes.ACC_SUPER, "Lib", null, "java/lang/Object", null); 380 cw.visitField(Opcodes.ACC_FINAL | Opcodes.ACC_STATIC, "ZCONST", "Z", null, Integer.MAX_VALUE); 381 cw.visitField(Opcodes.ACC_FINAL | Opcodes.ACC_STATIC, "SCONST", "S", null, Integer.MAX_VALUE); 382 jos.write(cw.toByteArray()); 383 } 384 385 ImmutableMap<String, String> input = 386 ImmutableMap.of( 387 "Test.java", 388 Joiner.on('\n') 389 .join( 390 "class Test {", 391 " static final short SCONST = Lib.SCONST + 0;", 392 " static final boolean ZCONST = Lib.ZCONST || false;", 393 "}")); 394 395 Map<String, byte[]> actual = IntegrationTestSupport.runTurbine(input, ImmutableList.of(lib)); 396 397 Map<String, Object> values = new LinkedHashMap<>(); 398 new ClassReader(actual.get("Test")) 399 .accept( 400 new ClassVisitor(Opcodes.ASM7) { 401 @Override 402 public FieldVisitor visitField( 403 int access, String name, String desc, String signature, Object value) { 404 values.put(name, value); 405 return super.visitField(access, name, desc, signature, value); 406 } 407 }, 408 0); 409 410 assertThat(values).containsEntry("SCONST", -1); 411 assertThat(values).containsEntry("ZCONST", 1); 412 } 413 414 @Test deprecated()415 public void deprecated() throws Exception { 416 BindingResult bound = 417 Binder.bind( 418 ImmutableList.of(Parser.parse("@Deprecated class Test {}")), 419 ClassPathBinder.bindClasspath(ImmutableList.of()), 420 TURBINE_BOOTCLASSPATH, 421 /* moduleVersion=*/ Optional.empty()); 422 Map<String, byte[]> lowered = 423 Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes(); 424 int[] acc = {0}; 425 new ClassReader(lowered.get("Test")) 426 .accept( 427 new ClassVisitor(Opcodes.ASM7) { 428 @Override 429 public void visit( 430 int version, 431 int access, 432 String name, 433 String signature, 434 String superName, 435 String[] interfaces) { 436 acc[0] = access; 437 } 438 }, 439 0); 440 assertThat((acc[0] & Opcodes.ACC_DEPRECATED)).isEqualTo(Opcodes.ACC_DEPRECATED); 441 } 442 443 @Test lazyImports()444 public void lazyImports() throws Exception { 445 ImmutableMap<String, String> sources = 446 ImmutableMap.<String, String>builder() 447 .put( 448 "b/B.java", 449 lines( 450 "package b;", // 451 "public class B {", 452 " public static class A {", 453 " public static final int X = 0;", 454 " }", 455 " public static class C {}", 456 "}")) 457 .put( 458 "anno/Anno.java", 459 lines( 460 "package anno;", // 461 "public @interface Anno {", 462 " int value() default 0;", 463 "}")) 464 .put( 465 "a/A.java", 466 lines( 467 "package a;", // 468 "import b.B;", 469 "import anno.Anno;", 470 "import static b.B.nosuch.A;", 471 "@Anno(A.X)", 472 "public class A extends B {", 473 " public A a;", 474 " public static final int X = 1;", 475 "}")) 476 .put( 477 "a/C.java", 478 lines( 479 "package c;", // 480 "import static b.B.nosuch.C;", 481 "class C {", 482 " C c;", 483 "}")) 484 .build(); 485 486 ImmutableMap<String, String> noImports; 487 { 488 ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); 489 sources.forEach( 490 (k, v) -> builder.put(k, v.replaceAll("import static b\\.B\\.nosuch\\..*;", ""))); 491 noImports = builder.build(); 492 } 493 494 Map<String, byte[]> expected = IntegrationTestSupport.runJavac(noImports, ImmutableList.of()); 495 Map<String, byte[]> actual = IntegrationTestSupport.runTurbine(sources, ImmutableList.of()); 496 assertThat(IntegrationTestSupport.dump(IntegrationTestSupport.sortMembers(actual))) 497 .isEqualTo(IntegrationTestSupport.dump(IntegrationTestSupport.canonicalize(expected))); 498 } 499 500 @Test missingOuter()501 public void missingOuter() throws Exception { 502 503 Map<String, byte[]> lib = 504 IntegrationTestSupport.runJavac( 505 ImmutableMap.of( 506 "A.java", 507 lines( 508 "interface A {", // 509 " interface M {", 510 " interface I {}", 511 " } ", 512 "}"), 513 "B.java", 514 lines( 515 "interface B extends A {", 516 " interface BM extends M {", 517 " interface BI extends I {}", 518 " }", 519 "}")), 520 ImmutableList.of()); 521 522 Path libJar = temporaryFolder.newFile("lib.jar").toPath(); 523 try (OutputStream os = Files.newOutputStream(libJar); 524 JarOutputStream jos = new JarOutputStream(os)) { 525 jos.putNextEntry(new JarEntry("A$M.class")); 526 jos.write(lib.get("A$M")); 527 jos.putNextEntry(new JarEntry("A$M$I.class")); 528 jos.write(lib.get("A$M$I")); 529 jos.putNextEntry(new JarEntry("B.class")); 530 jos.write(lib.get("B")); 531 jos.putNextEntry(new JarEntry("B$BM.class")); 532 jos.write(lib.get("B$BM")); 533 jos.putNextEntry(new JarEntry("B$BM$BI.class")); 534 jos.write(lib.get("B$BM$BI")); 535 } 536 537 ImmutableMap<String, String> sources = 538 ImmutableMap.<String, String>builder() 539 .put( 540 "Test.java", 541 lines( 542 "public class Test extends B.BM {", // 543 " I i;", 544 "}")) 545 .build(); 546 547 try { 548 IntegrationTestSupport.runTurbine(sources, ImmutableList.of(libJar)); 549 fail(); 550 } catch (TurbineError error) { 551 assertThat(error) 552 .hasMessageThat() 553 .contains("Test.java: error: could not locate class file for A"); 554 } 555 } 556 557 @Test missingOuter2()558 public void missingOuter2() throws Exception { 559 560 Map<String, byte[]> lib = 561 IntegrationTestSupport.runJavac( 562 ImmutableMap.of( 563 "A.java", 564 lines( 565 "class A {", // 566 " class M { ", 567 " class I {} ", 568 " } ", 569 "}"), 570 "B.java", 571 lines( 572 "class B extends A { ", 573 " class BM extends M { ", 574 " class BI extends I {} ", 575 " } ", 576 "}")), 577 ImmutableList.of()); 578 579 Path libJar = temporaryFolder.newFile("lib.jar").toPath(); 580 try (OutputStream os = Files.newOutputStream(libJar); 581 JarOutputStream jos = new JarOutputStream(os)) { 582 jos.putNextEntry(new JarEntry("A$M.class")); 583 jos.write(lib.get("A$M")); 584 jos.putNextEntry(new JarEntry("A$M$I.class")); 585 jos.write(lib.get("A$M$I")); 586 jos.putNextEntry(new JarEntry("B.class")); 587 jos.write(lib.get("B")); 588 jos.putNextEntry(new JarEntry("B$BM.class")); 589 jos.write(lib.get("B$BM")); 590 jos.putNextEntry(new JarEntry("B$BM$BI.class")); 591 jos.write(lib.get("B$BM$BI")); 592 } 593 594 ImmutableMap<String, String> sources = 595 ImmutableMap.<String, String>builder() 596 .put( 597 "Test.java", 598 lines( 599 "public class Test extends B {", // 600 " class M extends BM {", 601 " I i;", 602 " }", 603 "}")) 604 .build(); 605 606 try { 607 IntegrationTestSupport.runTurbine(sources, ImmutableList.of(libJar)); 608 fail(); 609 } catch (TurbineError error) { 610 assertThat(error) 611 .hasMessageThat() 612 .contains( 613 lines( 614 "Test.java:3: error: could not locate class file for A", 615 " I i;", 616 " ^")); 617 } 618 } 619 620 // If an element incorrectly has multiple visibility modifiers, pick one, and rely on javac to 621 // report a diagnostic. 622 @Test multipleVisibilities()623 public void multipleVisibilities() throws Exception { 624 ImmutableMap<String, String> sources = 625 ImmutableMap.of("Test.java", "public protected class Test {}"); 626 627 Map<String, byte[]> lowered = 628 IntegrationTestSupport.runTurbine(sources, /* classpath= */ ImmutableList.of()); 629 int[] testAccess = {0}; 630 new ClassReader(lowered.get("Test")) 631 .accept( 632 new ClassVisitor(Opcodes.ASM7) { 633 @Override 634 public void visit( 635 int version, 636 int access, 637 String name, 638 String signature, 639 String superName, 640 String[] interfaces) { 641 testAccess[0] = access; 642 } 643 }, 644 0); 645 assertThat((testAccess[0] & TurbineFlag.ACC_PUBLIC)).isEqualTo(TurbineFlag.ACC_PUBLIC); 646 assertThat((testAccess[0] & TurbineFlag.ACC_PROTECTED)).isNotEqualTo(TurbineFlag.ACC_PROTECTED); 647 } 648 lines(String... lines)649 static String lines(String... lines) { 650 return Joiner.on(System.lineSeparator()).join(lines); 651 } 652 } 653