package test.baseline; import static aQute.bnd.osgi.Constants.BUNDLE_SYMBOLICNAME; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.File; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.regex.Pattern; import aQute.bnd.build.Project; import aQute.bnd.build.ProjectBuilder; import aQute.bnd.build.Workspace; import aQute.bnd.differ.Baseline; import aQute.bnd.differ.Baseline.BundleInfo; import aQute.bnd.differ.Baseline.Info; import aQute.bnd.differ.DiffPluginImpl; import aQute.bnd.header.Attrs; import aQute.bnd.osgi.Builder; import aQute.bnd.osgi.Constants; import aQute.bnd.osgi.Jar; import aQute.bnd.osgi.Processor; import aQute.bnd.osgi.Verifier; import aQute.bnd.service.RepositoryPlugin; import aQute.bnd.service.diff.Delta; import aQute.bnd.service.diff.Diff; import aQute.bnd.service.diff.Tree; import aQute.bnd.version.Version; import aQute.lib.collections.SortedList; import aQute.lib.io.IO; import aQute.libg.reporter.ReporterAdapter; import junit.framework.TestCase; @SuppressWarnings("resource") public class BaselineTest extends TestCase { File tmp = new File("tmp").getAbsoluteFile(); Workspace workspace; public Workspace getWorkspace() throws Exception { if (workspace != null) return workspace; IO.delete(tmp); IO.copy(IO.getFile("testresources/ws"), tmp); return workspace = new Workspace(tmp); } public void tearDown() throws Exception { IO.delete(tmp); workspace = null; } /** * Test 2 jars compiled with different compilers */ public void testCompilerEnumDifference() throws Exception { DiffPluginImpl diff = new DiffPluginImpl(); try (Jar ecj = new Jar(IO.getFile("jar/baseline/com.example.baseline.ecj.jar")); Jar javac = new Jar(IO.getFile("jar/baseline/com.example.baseline.javac.jar"));) { Tree tecj = diff.tree(ecj); Tree tjavac = diff.tree(javac); Diff d = tecj.diff(tjavac); assertEquals(Delta.UNCHANGED, d.getDelta()); } } /** * Test skipping classes when there is source */ public void testClassesDiffWithSource() throws Exception { DiffPluginImpl diff = new DiffPluginImpl(); try (Jar jar = new Jar(IO.getFile("jar/osgi.jar")); Jar out = new Jar(".");) { out.putResource("OSGI-OPT/src/org/osgi/application/ApplicationContext.java", jar.getResource("OSGI-OPT/src/org/osgi/application/ApplicationContext.java")); out.putResource("org/osgi/application/ApplicationContext.class", jar.getResource("org/osgi/application/ApplicationContext.class")); Tree tree = diff.tree(out); Tree src = tree.get("<resources>") .get("OSGI-OPT/src/org/osgi/application/ApplicationContext.java") .getChildren()[0]; assertNotNull(src); assertNull(tree.get("<resources>").get("org/osgi/application/ApplicationContext.class")); } } public void testClassesDiffWithoutSource() throws Exception { DiffPluginImpl diff = new DiffPluginImpl(); try (Jar jar = new Jar(IO.getFile("jar/osgi.jar")); Jar out = new Jar(".");) { for (String path : jar.getResources().keySet()) { if (!path.startsWith("OSGI-OPT/src/")) out.putResource(path, jar.getResource(path)); } Tree tree = diff.tree(out); assertNull(tree.get("<resources>").get("OSGI-OPT/src/org/osgi/application/ApplicationContext.java")); assertNotNull(tree.get("<resources>").get("org/osgi/application/ApplicationContext.class")); } } public void testJava8DefaultMethods() throws Exception { try (Builder older = new Builder(); Builder newer = new Builder();) { older.addClasspath(IO.getFile("java8/older/bin")); older.setExportPackage("*"); newer.addClasspath(IO.getFile("java8/newer/bin")); newer.setExportPackage("*"); try (Jar o = older.build(); Jar n = newer.build();) { assertTrue(older.check()); assertTrue(newer.check()); DiffPluginImpl differ = new DiffPluginImpl(); Baseline baseline = new Baseline(older, differ); Set<Info> infoSet = baseline.baseline(n, o, null); assertEquals(1, infoSet.size()); for (Info info : infoSet) { assertTrue(info.mismatch); assertEquals(new Version(0, 1, 0), info.suggestedVersion); assertEquals(info.packageName, "api_default_methods"); } } } } /** * Check if we can ignore resources in the baseline. First build two jars * that are identical except for the b/b resource. Then do baseline on them. */ public void testIgnoreResourceDiff() throws Exception { Processor processor = new Processor(); DiffPluginImpl differ = new DiffPluginImpl(); differ.setIgnore("b/b"); Baseline baseline = new Baseline(processor, differ); try (Builder a = new Builder(); Builder b = new Builder();) { a.setProperty("-includeresource", "a/a;literal='aa',b/b;literal='bb'"); a.setProperty("-resourceonly", "true"); b.setProperty("-includeresource", "a/a;literal='aa',b/b;literal='bbb'"); b.setProperty("-resourceonly", "true"); try (Jar aj = a.build(); Jar bj = b.build();) { Set<Info> infoSet = baseline.baseline(aj, bj, null); BundleInfo binfo = baseline.getBundleInfo(); assertFalse(binfo.mismatch); } } } public static void testBaslineJar() throws Exception { // Workspace ws = new Workspace(IO.getFile("testresources/ws")); // // Project p3 = ws.getProject("p3"); // // ProjectBuilder builder = (ProjectBuilder) // p3.getBuilder(null).getSubBuilder(); // builder.setBundleSymbolicName("p3"); // // // Nothing specified // Jar jar = builder.getBaselineJar(false); // assertNull(jar); // // jar = builder.getBaselineJar(true); // assertEquals(".", jar.getName()); // // // Fallback to release repo // builder.set("-releaserepo", "Repo"); // jar = builder.getBaselineJar(false); // assertNull(jar); // // jar = builder.getBaselineJar(true); // assertEquals("p3", jar.getBsn()); // assertEquals("1.0.1", jar.getVersion()); // // // -baselinerepo specified // builder.set("-baselinerepo", "Release"); // jar = builder.getBaselineJar(false); // assertEquals("p3", jar.getBsn()); // assertEquals("1.2.0", jar.getVersion()); // // jar = builder.getBaselineJar(true); // assertEquals("p3", jar.getBsn()); // assertEquals("1.2.0", jar.getVersion()); // // // -baseline specified // builder.set("-baseline", "p3;version=1.1.0"); // jar = builder.getBaselineJar(false); // assertEquals("p3", jar.getBsn()); // assertEquals("1.1.0", jar.getVersion()); // // jar = builder.getBaselineJar(true); // assertEquals("p3", jar.getBsn()); // assertEquals("1.1.0", jar.getVersion()); } /** * When a JAR is build the manifest is not set in the resources but in a * instance var. * * @throws Exception */ public void testPrematureJar() throws Exception { File file = IO.getFile(new File(""), "jar/osgi.jar"); try (Builder b1 = new Builder(); Builder b2 = new Builder();) { b1.addClasspath(file); b1.setProperty(Constants.BUNDLE_VERSION, "1.0.0.${tstamp}"); b1.setExportPackage("org.osgi.service.event"); try (Jar j1 = b1.build();) { assertTrue(b1.check()); File tmp = new File("tmp.jar"); j1.write(tmp); try (Jar j11 = new Jar(tmp);) { Thread.sleep(2000); b2.addClasspath(file); b2.setProperty(Constants.BUNDLE_VERSION, "1.0.0.${tstamp}"); b2.setExportPackage("org.osgi.service.event"); try (Jar j2 = b2.build();) { assertTrue(b2.check()); DiffPluginImpl differ = new DiffPluginImpl(); ReporterAdapter ra = new ReporterAdapter(); Baseline baseline = new Baseline(ra, differ); ra.setTrace(true); ra.setPedantic(true); Set<Info> infos = baseline.baseline(j2, j11, null); print(baseline.getDiff(), " "); assertEquals(Delta.UNCHANGED, baseline.getDiff().getDelta()); } } finally { tmp.delete(); } } } } static Pattern VERSION_HEADER_P = Pattern.compile("Bundle-Header:(" + Verifier.VERSION_STRING + ")", Pattern.CASE_INSENSITIVE); void print(Diff diff, String indent) { if (diff.getDelta() == Delta.UNCHANGED) return; System.out.println(indent + " " + diff); for (Diff sub : diff.getChildren()) { print(sub, indent + " "); } } /** * In repo: * * <pre> * p3-1.1.0.jar p3-1.2.0.jar * </pre> * * @throws Exception */ public void testRepository() throws Exception { Jar v1_2_0_a = mock(Jar.class); when(v1_2_0_a.getVersion()).thenReturn("1.2.0.b"); when(v1_2_0_a.getBsn()).thenReturn("p3"); RepositoryPlugin repo = mock(RepositoryPlugin.class); getWorkspace().addBasicPlugin(repo); @SuppressWarnings("unchecked") Map<String,String> map = any(Map.class); when(repo.get(anyString(), any(Version.class), map)) .thenReturn(IO.getFile("testresources/ws/cnf/releaserepo/p3/p3-1.2.0.jar")); System.out.println(repo.get("p3", new Version("1.2.0.b"), new Attrs())); when(repo.canWrite()).thenReturn(true); when(repo.getName()).thenReturn("Baseline"); when(repo.versions("p3")).thenReturn(new SortedList<Version>(new Version("1.1.0.a"), new Version("1.1.0.b"), new Version("1.2.0.a"), new Version("1.2.0.b"))); Project p3 = getWorkspace().getProject("p3"); p3.setBundleVersion("1.3.0"); ProjectBuilder builder = (ProjectBuilder) p3.getBuilder(null).getSubBuilder(); builder.setProperty(Constants.BASELINE, "*"); builder.setProperty(Constants.BASELINEREPO, "Baseline"); // Nothing specified Jar jar = builder.getBaselineJar(); assertEquals("1.2.0", new Version(jar.getVersion()).getWithoutQualifier().toString()); if (!builder.check()) fail(builder.getErrors().toString()); { // check for error when repository contains later versions builder = (ProjectBuilder) p3.getBuilder(null).getSubBuilder(); builder.setBundleVersion("1.1.3"); builder.setTrace(true); builder.setProperty(Constants.BASELINE, "*"); builder.setProperty(Constants.BASELINEREPO, "Baseline"); jar = builder.getBaselineJar(); assertNull(jar); if (!builder.check("The baseline version 1.2.0.b is higher than the current version 1.1.3 for p3")) fail(builder.getErrors().toString()); } { // check for no error when repository has the same version builder = (ProjectBuilder) p3.getBuilder(null).getSubBuilder(); builder.setBundleVersion("1.2.0.b"); builder.setTrace(true); builder.setProperty(Constants.BASELINE, "*"); builder.setProperty(Constants.BASELINEREPO, "Baseline"); jar = builder.getBaselineJar(); assertNotNull(jar); if (!builder.check()) fail(builder.getErrors().toString()); } { // check for no error when repository has the same version builder = (ProjectBuilder) p3.getBuilder(null).getSubBuilder(); builder.setBundleVersion("1.2.0.b"); builder.setTrace(true); builder.setProperty(Constants.BASELINE, "*"); builder.setProperty(Constants.BASELINEREPO, "Baseline"); builder.build(); if (!builder.check("The bundle version \\(1.2.0/1.2.0\\) is too low, must be at least 1.3.0")) fail(builder.getErrors().toString()); } } /** * Check what happens when there is nothing in the repo ... We do not * generate an error when version <=1.0.0, otherwise we generate an error. * * @throws Exception */ public void testNothingInRepo() throws Exception { File tmp = new File("tmp"); tmp.mkdirs(); try { RepositoryPlugin repo = mock(RepositoryPlugin.class); when(repo.canWrite()).thenReturn(true); when(repo.getName()).thenReturn("Baseline"); when(repo.versions("p3")).thenReturn(new TreeSet<Version>()); getWorkspace().addBasicPlugin(repo); Project p3 = getWorkspace().getProject("p3"); p3.setProperty(Constants.BASELINE, "*"); p3.setProperty(Constants.BASELINEREPO, "Baseline"); p3.setBundleVersion("0"); p3.build(); assertTrue(p3.check()); p3.setBundleVersion("1.0.0.XXXXXX"); p3.build(); assertTrue(p3.check()); p3.setBundleVersion("5"); p3.build(); assertTrue(p3.check("There is no baseline for p3 in the baseline repo")); } finally { IO.delete(tmp); } } // Adding a method to a ProviderType produces a MINOR bump (1.0.0 -> 1.1.0) public void testProviderTypeBump() throws Exception { Processor processor = new Processor(); DiffPluginImpl differ = new DiffPluginImpl(); Baseline baseline = new Baseline(processor, differ); try (Jar older = new Jar(IO.getFile("testresources/api-orig.jar")); Jar newer = new Jar(IO.getFile("testresources/api-providerbump.jar"));) { Set<Info> infoSet = baseline.baseline(newer, older, null); System.out.println(differ.tree(newer).get("<api>")); assertEquals(1, infoSet.size()); Info info = infoSet.iterator().next(); assertTrue(info.mismatch); assertEquals("dummy.api", info.packageName); assertEquals("1.1.0", info.suggestedVersion.toString()); } } // Adding a method to a ConsumerType produces a MINOR bump (1.0.0 -> 2.0.0) public static void testConsumerTypeBump() throws Exception { Processor processor = new Processor(); DiffPluginImpl differ = new DiffPluginImpl(); Baseline baseline = new Baseline(processor, differ); try (Jar older = new Jar(IO.getFile("testresources/api-orig.jar")); Jar newer = new Jar(IO.getFile("testresources/api-consumerbump.jar"));) { Set<Info> infoSet = baseline.baseline(newer, older, null); assertEquals(1, infoSet.size()); Info info = infoSet.iterator().next(); assertTrue(info.mismatch); assertEquals("dummy.api", info.packageName); assertEquals("2.0.0", info.suggestedVersion.toString()); } } // Adding a method to a ProviderType produces a MINOR bump (1.0.0 -> 1.1.0) public void testBundleVersionBump() throws Exception { Processor processor = new Processor(); DiffPluginImpl differ = new DiffPluginImpl(); Baseline baseline = new Baseline(processor, differ); try (Jar older = new Jar(IO.getFile("testresources/api-orig.jar")); Jar newer = new Jar(IO.getFile("testresources/api-providerbump.jar"));) { baseline.baseline(newer, older, null); BundleInfo bundleInfo = baseline.getBundleInfo(); assertTrue(bundleInfo.mismatch); assertEquals("1.1.0", bundleInfo.suggestedVersion.toString()); } } // Adding a method to a ProviderType produces a MINOR bump (1.0.0 -> 1.1.0) public void testBundleVersionBumpDifferentSymbolicNames() throws Exception { Processor processor = new Processor(); DiffPluginImpl differ = new DiffPluginImpl(); Baseline baseline = new Baseline(processor, differ); try (Jar older = new Jar(IO.getFile("testresources/api-orig.jar")); Jar newer = new Jar(IO.getFile("testresources/api-providerbump.jar"));) { newer.getManifest().getMainAttributes().putValue(BUNDLE_SYMBOLICNAME, "a.different.name"); baseline.baseline(newer, older, null); BundleInfo bundleInfo = baseline.getBundleInfo(); assertFalse(bundleInfo.mismatch); assertEquals(newer.getVersion(), bundleInfo.suggestedVersion.toString()); } } // Adding a method to an exported class produces a MINOR bump (1.0.0 -> 1.1.0) public void testMinorChange() throws Exception { Processor processor = new Processor(); DiffPluginImpl differ = new DiffPluginImpl(); Baseline baseline = new Baseline(processor, differ); try (Jar older = new Jar(IO.getFile("testresources/minor-and-removed-change-1.0.0.jar")); Jar newer = new Jar(IO.getFile("testresources/minor-change-1.0.1.jar"));) { baseline.baseline(newer, older, null); BundleInfo bundleInfo = baseline.getBundleInfo(); assertTrue(bundleInfo.mismatch); assertEquals("1.1.0", bundleInfo.suggestedVersion.toString()); } } // Adding a method to an exported class and unexporting a package produces a MINOR bump (1.0.0 -> 1.1.0) public void testMinorAndRemovedChange() throws Exception { Processor processor = new Processor(); DiffPluginImpl differ = new DiffPluginImpl(); Baseline baseline = new Baseline(processor, differ); try (Jar older = new Jar(IO.getFile("testresources/minor-and-removed-change-1.0.0.jar")); Jar newer = new Jar(IO.getFile("testresources/minor-and-removed-change-1.0.1.jar"));) { baseline.baseline(newer, older, null); BundleInfo bundleInfo = baseline.getBundleInfo(); assertTrue(bundleInfo.mismatch); assertEquals("2.0.0", bundleInfo.suggestedVersion.toString()); } } // Deleting a protected field on a ProviderType API class produces a MINOR // bump (1.0.0 -> 1.1.0) public void testProviderProtectedFieldRemovedChange() throws Exception { Processor processor = new Processor(); DiffPluginImpl differ = new DiffPluginImpl(); Baseline baseline = new Baseline(processor, differ); try (Jar older = new Jar(IO.getFile("jar/baseline/provider-deletion-1.0.0.jar")); Jar newer = new Jar(IO.getFile("jar/baseline/provider-deletion-1.1.0.jar"));) { baseline.baseline(newer, older, null); BundleInfo bundleInfo = baseline.getBundleInfo(); assertFalse(bundleInfo.mismatch); assertEquals("1.1.0", bundleInfo.suggestedVersion.toString()); Set<Info> packageInfos = baseline.getPackageInfos(); assertEquals(1, packageInfos.size()); Info change = packageInfos.iterator().next(); assertTrue(change.mismatch); assertEquals("bnd.baseline.test", change.packageName); assertEquals("1.1.0", change.suggestedVersion.toString()); } } // Moving a package from the root into a jar on the Bundle-ClassPath // should not result in DELETED public void testMovePackageToBundleClassPath() throws Exception { Processor processor = new Processor(); DiffPluginImpl differ = new DiffPluginImpl(); Baseline baseline = new Baseline(processor, differ); try (Jar older = new Jar(IO.getFile("jar/baseline/com.liferay.calendar.api-2.0.5.jar")); Jar newer = new Jar(IO.getFile("jar/baseline/com.liferay.calendar.api-2.1.0.jar"));) { baseline.baseline(newer, older, null); BundleInfo bundleInfo = baseline.getBundleInfo(); assertFalse(bundleInfo.mismatch); assertEquals("2.1.0", bundleInfo.suggestedVersion.toString()); Set<Info> packageInfos = baseline.getPackageInfos(); assertEquals(12, packageInfos.size()); Info change = packageInfos.iterator().next(); assertFalse(change.mismatch); assertEquals("com.google.ical.iter", change.packageName); assertEquals("20110304.0.0", change.suggestedVersion.toString()); } } // This tests the scenario where a super type is injected into the class // hierarchy but the super class comes from outside the bundle so that the // baseline cannot find it. Since the class hierarchy was cut off, the // baseline would _forget_ that every class inherits from Object, and _lose_ // Object's methods if not directly implemented. public void testCutOffInheritance() throws Exception { Processor processor = new Processor(); DiffPluginImpl differ = new DiffPluginImpl(); Baseline baseline = new Baseline(processor, differ); try (Jar older = new Jar(IO.getFile("jar/baseline/inheritance-change-1.0.0.jar")); Jar newer = new Jar(IO.getFile("jar/baseline/inheritance-change-1.1.0.jar"));) { baseline.baseline(newer, older, null); BundleInfo bundleInfo = baseline.getBundleInfo(); assertFalse(bundleInfo.mismatch); assertEquals("1.1.0", bundleInfo.suggestedVersion.toString()); Set<Info> packageInfos = baseline.getPackageInfos(); assertEquals(1, packageInfos.size()); Info change = packageInfos.iterator().next(); assertFalse(change.mismatch); assertEquals("example", change.packageName); assertEquals("1.1.0", change.suggestedVersion.toString()); Diff packageDiff = change.packageDiff; Collection< ? extends Diff> children = packageDiff.getChildren(); assertEquals(5, children.size()); Iterator< ? extends Diff> iterator = children.iterator(); Diff diff = iterator.next(); assertEquals(Delta.MICRO, diff.getDelta()); diff = iterator.next(); assertEquals(Delta.MICRO, diff.getDelta()); diff = iterator.next(); assertEquals(Delta.MINOR, diff.getDelta()); } } }