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());
}
}
}