You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by pa...@apache.org on 2020/01/15 12:41:11 UTC

[sling-org-apache-sling-feature] branch issues/SLING-8998 created (now ca4b9b4)

This is an automated email from the ASF dual-hosted git repository.

pauls pushed a change to branch issues/SLING-8998
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature.git.


      at ca4b9b4  SLING-8998: Add feature-origins metadata entry to artifacts in features when merging

This branch includes the following new commits:

     new ca4b9b4  SLING-8998: Add feature-origins metadata entry to artifacts in features when merging

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[sling-org-apache-sling-feature] 01/01: SLING-8998: Add feature-origins metadata entry to artifacts in features when merging

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pauls pushed a commit to branch issues/SLING-8998
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature.git

commit ca4b9b47c9c5f4895ee9add2f4f8bd6308d2cd7a
Author: Karl Pauls <ka...@gmail.com>
AuthorDate: Wed Jan 15 13:40:48 2020 +0100

    SLING-8998: Add feature-origins metadata entry to artifacts in features when merging
---
 .../apache/sling/feature/builder/BuilderUtil.java  |  72 ++++++++++--
 .../sling/feature/builder/FeatureBuilder.java      |   9 ++
 .../sling/feature/builder/BuilderUtilTest.java     |  11 ++
 .../sling/feature/builder/FeatureBuilderTest.java  | 128 +++++++++++++--------
 4 files changed, 165 insertions(+), 55 deletions(-)

diff --git a/src/main/java/org/apache/sling/feature/builder/BuilderUtil.java b/src/main/java/org/apache/sling/feature/builder/BuilderUtil.java
index febf000..0651243 100644
--- a/src/main/java/org/apache/sling/feature/builder/BuilderUtil.java
+++ b/src/main/java/org/apache/sling/feature/builder/BuilderUtil.java
@@ -19,6 +19,7 @@ package org.apache.sling.feature.builder;
 import java.io.StringReader;
 import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -166,7 +167,7 @@ class BuilderUtil {
                     selectedArtifacts.add(count++, existing);
                     selectedArtifacts.add(artifactFromSource);
                 } else {
-                    List<Artifact> artifacts = selectArtifactOverride(existing, artifactFromSource, artifactOverrides);
+                    List<Artifact> artifacts = selectArtifactOverride(existing, artifactFromSource, artifactOverrides, sourceFeature.getId().toMvnId());
                     // if we have an all policy we might have more then one artifact - we put the target one first
                     if (artifacts.size() > 1) {
                         selectedArtifacts.add(count++, artifacts.remove(0));
@@ -187,7 +188,7 @@ class BuilderUtil {
 
             for (final Artifact sa : new LinkedHashSet<>(selectedArtifacts)) {
                 // create a copy to detach artifact from source
-                final Artifact cp = sa.copy(sa.getId());
+                final Artifact cp = addFeatureOrigin(sourceFeature, sa.copy(sa.getId()));
                 // Record the original feature of the bundle, if needed
                 if (originKey != null) {
                     if (sourceFeature != null && source.contains(sa) && sa.getMetadata().get(originKey) == null) {
@@ -206,10 +207,15 @@ class BuilderUtil {
     }
 
     static List<Artifact> selectArtifactOverride(Artifact fromTarget, Artifact fromSource,
-            List<ArtifactId> artifactOverrides) {
+        List<ArtifactId> artifactOverrides) {
+        return selectArtifactOverride(fromTarget, fromSource, artifactOverrides, null);
+    }
+
+    static List<Artifact> selectArtifactOverride(Artifact fromTarget, Artifact fromSource,
+            List<ArtifactId> artifactOverrides, String sourceID) {
         if (fromTarget.getId().equals(fromSource.getId())) {
             // They're the same so return the source (latest)
-            return Collections.singletonList(selectStartOrder(fromTarget, fromSource, fromSource));
+            return Collections.singletonList(addFeatureOrigin(selectStartOrder(fromTarget, fromSource, fromSource), fromTarget, fromSource));
         }
 
         final Set<ArtifactId> commonPrefixes = getCommonArtifactIds(fromTarget, fromSource);
@@ -219,6 +225,11 @@ class BuilderUtil {
         }
 
         final List<Artifact> result = new ArrayList<>();
+        if (artifactOverrides.isEmpty() && getFeatureOrigins(fromTarget).contains(sourceID)) {
+            result.add(fromTarget);
+            result.add(addFeatureOrigin(fromSource, fromTarget, fromSource));
+            return result;
+        }
         outer: for (ArtifactId prefix : commonPrefixes) {
             for (final ArtifactId override : artifactOverrides) {
                 if (match(prefix, override)) {
@@ -230,21 +241,24 @@ class BuilderUtil {
                     } else if (BuilderContext.VERSION_OVERRIDE_HIGHEST.equalsIgnoreCase(rule)) {
                         Version a1v = fromTarget.getId().getOSGiVersion();
                         Version a2v = fromSource.getId().getOSGiVersion();
-                        result.add(selectStartOrder(fromTarget, fromSource, a1v.compareTo(a2v) > 0 ? fromTarget : fromSource));
+                        result.add(
+                            addFeatureOrigin(
+                                selectStartOrder(fromTarget, fromSource, a1v.compareTo(a2v) > 0 ? fromTarget : fromSource),
+                                fromTarget, fromSource));
                     } else if (BuilderContext.VERSION_OVERRIDE_LATEST.equalsIgnoreCase(rule)) {
-                        result.add(selectStartOrder(fromTarget, fromSource, fromSource));
+                        result.add(addFeatureOrigin(selectStartOrder(fromTarget, fromSource, fromSource), fromTarget, fromSource));
                     } else {
 
                         // The rule must represent a version
                         // See if its one of the existing artifact. If so use those, as they may have
                         // additional metadata
                         if (fromTarget.getId().getVersion().equals(rule)) {
-                            result.add(selectStartOrder(fromTarget, fromSource, fromTarget));
+                            result.add(addFeatureOrigin(selectStartOrder(fromTarget, fromSource, fromTarget), fromTarget, fromSource));
                         } else if (fromSource.getId().getVersion().equals(rule)) {
-                            result.add(selectStartOrder(fromTarget, fromSource, fromSource));
+                            result.add(addFeatureOrigin(selectStartOrder(fromTarget, fromSource, fromSource), fromTarget, fromSource));
                         } else {
                             // It's a completely new artifact
-                            result.add(selectStartOrder(fromTarget, fromSource, new Artifact(override)));
+                            result.add(addFeatureOrigin(selectStartOrder(fromTarget, fromSource, new Artifact(override)), fromTarget, fromSource));
                         }
                     }
                     break outer;
@@ -282,6 +296,46 @@ class BuilderUtil {
         }
     }
 
+    private static Artifact addFeatureOrigin(Artifact target, Artifact... artifacts) {
+        return addFeatureOrigin(null, target, artifacts);
+    }
+
+    private static Artifact addFeatureOrigin(Feature feature, Artifact target, Artifact... artifacts) {
+        LinkedHashSet<String> originFeatures = new LinkedHashSet<>();
+        if (artifacts != null && artifacts.length > 0) {
+            for (Artifact artifact : artifacts) {
+                originFeatures.addAll(getFeatureOrigins(artifact));
+            }
+        }
+        else {
+            originFeatures.addAll(getFeatureOrigins(target));
+        }
+        if (feature != null) {
+            originFeatures.add(feature.getId().toMvnId());
+        }
+        String origins = String.join(",", originFeatures);
+        if (origins.equals(target.getMetadata().get("feature-origins"))) {
+            return target;
+        }
+        else {
+            Artifact result = target.copy(target.getId());
+            result.getMetadata().put("feature-origins", origins);
+            return result;
+        }
+    }
+
+    private static LinkedHashSet<String> getFeatureOrigins(Artifact artifact) {
+        String origins = artifact.getMetadata().get("feature-origins");
+        LinkedHashSet<String> originFeatures;
+        if (origins == null) {
+            originFeatures = new LinkedHashSet<>();
+        }
+        else {
+            originFeatures =  new LinkedHashSet<>(Arrays.asList(origins.split(",")));
+        }
+        return originFeatures;
+    }
+
     private static boolean match(final ArtifactId id, final ArtifactId override) {
         int matchCount = 0;
         // check group id
diff --git a/src/main/java/org/apache/sling/feature/builder/FeatureBuilder.java b/src/main/java/org/apache/sling/feature/builder/FeatureBuilder.java
index 83de762..5566e0f 100644
--- a/src/main/java/org/apache/sling/feature/builder/FeatureBuilder.java
+++ b/src/main/java/org/apache/sling/feature/builder/FeatureBuilder.java
@@ -17,9 +17,11 @@
 package org.apache.sling.feature.builder;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -334,6 +336,13 @@ public abstract class FeatureBuilder {
 
             for (Artifact a : result.getBundles()) {
                 a.getMetadata().remove(TRACKING_KEY);
+                String origins = a.getMetadata().get("feature-origins");
+                if (origins != null && !origins.isEmpty()) {
+                    LinkedHashSet<String> originList = new LinkedHashSet<>(Arrays.asList(origins.split(",")));
+                    originList.remove(prototypeFeature.getId().toMvnId());
+                    originList.add(feature.getId().toMvnId());
+                    a.getMetadata().put("feature-origins", String.join(",", originList));
+                }
             }
             for (Extension e : result.getExtensions()) {
                 if (ExtensionType.ARTIFACTS == e.getType()) {
diff --git a/src/test/java/org/apache/sling/feature/builder/BuilderUtilTest.java b/src/test/java/org/apache/sling/feature/builder/BuilderUtilTest.java
index e4f41a7..b132e99 100644
--- a/src/test/java/org/apache/sling/feature/builder/BuilderUtilTest.java
+++ b/src/test/java/org/apache/sling/feature/builder/BuilderUtilTest.java
@@ -790,4 +790,15 @@ public class BuilderUtilTest {
 
         return a;
     }
+
+    @SafeVarargs
+    public static Artifact createBundle(final String id, Map.Entry<String, String> ... metadata) {
+        final Artifact a = new Artifact(ArtifactId.parse(id));
+
+        for (Map.Entry<String, String> md : metadata) {
+            a.getMetadata().put(md.getKey(), md.getValue());
+        }
+
+        return a;
+    }
 }
diff --git a/src/test/java/org/apache/sling/feature/builder/FeatureBuilderTest.java b/src/test/java/org/apache/sling/feature/builder/FeatureBuilderTest.java
index 53ed1ce..a5ec305 100644
--- a/src/test/java/org/apache/sling/feature/builder/FeatureBuilderTest.java
+++ b/src/test/java/org/apache/sling/feature/builder/FeatureBuilderTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.util.AbstractMap;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -211,7 +212,7 @@ public class FeatureBuilderTest {
         b.getBundles().add(BuilderUtilTest.createBundle("o/a/6.0.0", 10));
 
         Feature ab = new Feature(ArtifactId.fromMvnId("g:ab:1"));
-        ab.getBundles().add(BuilderUtilTest.createBundle("o/a/6.0.0", 8));
+        ab.getBundles().add(BuilderUtilTest.createBundle("o/a/6.0.0", 8, new AbstractMap.SimpleEntry("feature-origins", a.getId() + "," + b.getId())));
 
         Feature assembled = FeatureBuilder.assemble(ArtifactId.fromMvnId("g:ab:1"), new BuilderContext(provider)
                 .addArtifactsOverride(ArtifactId.fromMvnId("o:a:HIGHEST")), a, b);
@@ -219,9 +220,6 @@ public class FeatureBuilderTest {
 
         equals(ab, assembled );
 
-        ab = new Feature(ArtifactId.fromMvnId("g:ab:1"));
-        ab.getBundles().add(BuilderUtilTest.createBundle("o/a/6.0.0", 8));
-
         assembled = FeatureBuilder.assemble(ArtifactId.fromMvnId("g:ab:1"), new BuilderContext(provider)
             .addArtifactsOverride(ArtifactId.fromMvnId("o:a:LATEST")), a, b);
         assembled.getExtensions().clear();
@@ -229,8 +227,12 @@ public class FeatureBuilderTest {
         equals(ab, assembled );
 
         ab = new Feature(ArtifactId.fromMvnId("g:ab:1"));
-        ab.getBundles().addAll(a.getBundles());
-        ab.getBundles().addAll(b.getBundles());
+        for (Artifact bundle : a.getBundles()) {
+            ab.getBundles().add(BuilderUtilTest.createBundle(bundle.getId().toMvnId(), bundle.getStartOrder(), new AbstractMap.SimpleEntry("feature-origins", a.getId() + "," + b.getId())));
+        }
+        for (Artifact bundle : b.getBundles()) {
+            ab.getBundles().add(BuilderUtilTest.createBundle(bundle.getId().toMvnId(), bundle.getStartOrder(), new AbstractMap.SimpleEntry("feature-origins", b.getId().toMvnId())));
+        }
 
         assembled = FeatureBuilder.assemble(ArtifactId.fromMvnId("g:ab:1"), new BuilderContext(provider)
             .addArtifactsOverride(ArtifactId.fromMvnId("o:a:ALL")), a, b);
@@ -242,7 +244,7 @@ public class FeatureBuilderTest {
         a.getBundles().get(1).setStartOrder(1);
 
         ab = new Feature(ArtifactId.fromMvnId("g:ab:1"));
-        ab.getBundles().add(BuilderUtilTest.createBundle("o/a/6.0.0", 1));
+        ab.getBundles().add(BuilderUtilTest.createBundle("o/a/6.0.0", 1, new AbstractMap.SimpleEntry("feature-origins", a.getId() + "," + b.getId())));
         ab.getBundles().get(0).setStartOrder(1);
 
         assembled = FeatureBuilder.assemble(ArtifactId.fromMvnId("g:ab:1"), new BuilderContext(provider)
@@ -253,6 +255,35 @@ public class FeatureBuilderTest {
         equals(ab, assembled);
     }
 
+    @Test public void testMergeMultipleVersionsNoConflict() {
+        Feature a = new Feature(ArtifactId.fromMvnId("g:a:1"));
+        Feature b = new Feature(ArtifactId.fromMvnId("g:b:1"));
+
+
+        a.getBundles().add(BuilderUtilTest.createBundle("o/a/1.0.0", 10));
+        a.getBundles().add(BuilderUtilTest.createBundle("o/a/2.0.0", 9));
+        a.getBundles().add(BuilderUtilTest.createBundle("o/a/3.0.0", 11));
+
+        b.getBundles().add(BuilderUtilTest.createBundle("o/b    /4.0.0", 8));
+        b.getBundles().add(BuilderUtilTest.createBundle("o/b/5.0.0", 12));
+        b.getBundles().add(BuilderUtilTest.createBundle("o/b/6.0.0", 10));
+
+        Feature ab = new Feature(ArtifactId.fromMvnId("g:ab:1"));
+        ab.getBundles().add(BuilderUtilTest.createBundle("o/a/1.0.0", 10, new AbstractMap.SimpleEntry("feature-origins", a.getId().toMvnId())));
+        ab.getBundles().add(BuilderUtilTest.createBundle("o/a/2.0.0", 9, new AbstractMap.SimpleEntry("feature-origins", a.getId().toMvnId())));
+        ab.getBundles().add(BuilderUtilTest.createBundle("o/a/3.0.0", 11, new AbstractMap.SimpleEntry("feature-origins", a.getId().toMvnId())));
+
+        ab.getBundles().add(BuilderUtilTest.createBundle("o/b/4.0.0", 8, new AbstractMap.SimpleEntry("feature-origins", b.getId().toMvnId())));
+        ab.getBundles().add(BuilderUtilTest.createBundle("o/b/5.0.0", 12, new AbstractMap.SimpleEntry("feature-origins", b.getId().toMvnId())));
+        ab.getBundles().add(BuilderUtilTest.createBundle("o/b/6.0.0", 10, new AbstractMap.SimpleEntry("feature-origins", b.getId().toMvnId())));
+
+
+        Feature assembled = FeatureBuilder.assemble(ArtifactId.fromMvnId("g:ab:1"), new BuilderContext(provider), a, b);
+        assembled.getExtensions().clear();
+
+        equals(ab, assembled);
+    }
+
 
     @Test public void testNoIncludesNoUpgrade() throws Exception {
         final Feature base = new Feature(ArtifactId.parse("org.apache.sling/test-feature/1.1"));
@@ -353,18 +384,20 @@ public class FeatureBuilderTest {
         result.setPrototype(null);
         result.getBundles().clear();
 
-        result.getBundles().add(BuilderUtilTest.createBundle("org.apache.sling/foo-bar/4.5.6", 3));
-        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewversion_low/2", 5));
-        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewversion_low/1", 5));
-        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewversion_high/2", 5));
-        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewversion_high/5", 5));
-        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewstartlevel/1", 5));
-        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewstartlevelandversion/1", 5));
-        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewstartlevelandversion/2", 10));
-        result.getBundles().add(a1.copy(a1.getId()));
-        result.getBundles().add(BuilderUtilTest.createBundle("org.apache.sling/application-bundle/2.0.0", 1));
-        result.getBundles().add(BuilderUtilTest.createBundle("org.apache.sling/another-bundle/2.1.0", 1));
-        result.getBundles().add(BuilderUtilTest.createBundle("org.apache.sling/foo-xyz/1.2.3", 2));
+        result.getBundles().add(BuilderUtilTest.createBundle("org.apache.sling/foo-bar/4.5.6", 3, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
+        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewversion_low/2", 5, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
+        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewversion_low/1", 5, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
+        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewversion_high/2", 5, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
+        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewversion_high/5", 5, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
+        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewstartlevel/1", 5, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
+        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewstartlevelandversion/1", 5, new AbstractMap.SimpleEntry("feature-origins",base.getId().toMvnId())));
+        result.getBundles().add(BuilderUtilTest.createBundle("group/testnewstartlevelandversion/2", 10, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
+        Artifact copy = a1.copy(a1.getId());
+        copy.getMetadata().put("feature-origins", base.getId().toMvnId());
+        result.getBundles().add(copy);
+        result.getBundles().add(BuilderUtilTest.createBundle("org.apache.sling/application-bundle/2.0.0", 1, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
+        result.getBundles().add(BuilderUtilTest.createBundle("org.apache.sling/another-bundle/2.1.0", 1, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
+        result.getBundles().add(BuilderUtilTest.createBundle("org.apache.sling/foo-xyz/1.2.3", 2, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
 
         result.getVariables().put("varx", "myvalx");
         result.getFrameworkProperties().put("bar", "X");
@@ -392,24 +425,34 @@ public class FeatureBuilderTest {
 
         Feature result = new Feature(ArtifactId.parse("g:tgtart:1"));
 
-        result.getBundles().add(BuilderUtilTest.createBundle("group/testmulti/2", 8));
-
-
-        Artifact b1 = new Artifact(ArtifactId.fromMvnId("group:testmulti:1"));
+        result.getBundles().add(BuilderUtilTest.createBundle("group/testmulti/2", 8, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
+        Artifact b1 = BuilderUtilTest.createBundle("group:testmulti:1", new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b1);
 
 
-        Artifact b2 = new Artifact(ArtifactId.fromMvnId("group:testmulti:3"));
+        Artifact b2 = BuilderUtilTest.createBundle("group:testmulti:3", new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b2);
 
-        Artifact b3 = new Artifact(ArtifactId.fromMvnId("group:someart:1.2.3"));
+        Artifact b3 = BuilderUtilTest.createBundle("group:someart:1.2.3", new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         b3.setStartOrder(4);
         result.getBundles().add(b3);
-        Artifact b0 = new Artifact(ArtifactId.fromMvnId("g:myart:1"));
+        Artifact b0 = BuilderUtilTest.createBundle("g:myart:1", new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b0);
 
 
         equals(result, assembled);
+
+        Feature addOn = new Feature(ArtifactId.fromMvnId("g:addon:1"));
+        addOn.getBundles().add(BuilderUtilTest.createBundle("group:someart:1.2.3"));
+        assembled = FeatureBuilder.assemble(ArtifactId.fromMvnId("g:tgtart:2"), builderContext, assembled, addOn);
+        assembled.getExtensions().clear();
+
+        result = result.copy(ArtifactId.fromMvnId("g:tgtart:2"));
+        int idx = result.getBundles().indexOf(BuilderUtilTest.createBundle("group:someart:1.2.3"));
+        result.getBundles().remove(idx);
+        result.getBundles().add(idx, BuilderUtilTest.createBundle("group:someart:1.2.3", 4, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId() + "," + addOn.getId())));
+
+        equals(result, assembled);
     }
 
     @Test public void testSingleIncludeMultiVersion2() {
@@ -422,16 +465,13 @@ public class FeatureBuilderTest {
         Feature assembled = FeatureBuilder.assemble(base, builderContext);
 
         Feature result = new Feature(ArtifactId.parse("g:tgtart:1"));
-        Artifact b1 = new Artifact(ArtifactId.fromMvnId("group:testmulti:1"));
-        b1.setStartOrder(4);
+        Artifact b1 = BuilderUtilTest.createBundle("group:testmulti:1" ,4, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b1);
-        Artifact b2 = new Artifact(ArtifactId.fromMvnId("group:testmulti:2"));
-        b2.setStartOrder(8);
+        Artifact b2 = BuilderUtilTest.createBundle("group:testmulti:2",8, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b2);
-        Artifact b3 = new Artifact(ArtifactId.fromMvnId("group:someart:1.2.3"));
-        b3.setStartOrder(4);
+        Artifact b3 = BuilderUtilTest.createBundle("group:someart:1.2.3",4, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b3);
-        Artifact b0 = new Artifact(ArtifactId.fromMvnId("g:myart:1"));
+        Artifact b0 = BuilderUtilTest.createBundle("g:myart:1", new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b0);
 
         equals(result, assembled);
@@ -449,15 +489,13 @@ public class FeatureBuilderTest {
 
         Feature result = new Feature(ArtifactId.parse("g:tgtart:1"));
 
-        result.getBundles().add(BuilderUtilTest.createBundle("group/testmulti/2", 8));
+        result.getBundles().add(BuilderUtilTest.createBundle("group/testmulti/2", 8, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
 
-        Artifact b1 = new Artifact(ArtifactId.fromMvnId("group:testmulti:1"));
-        b1.setStartOrder(4);
+        Artifact b1 = BuilderUtilTest.createBundle("group:testmulti:1", 4, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b1);
-        Artifact b3 = new Artifact(ArtifactId.fromMvnId("group:someart:1.2.3"));
-        b3.setStartOrder(4);
+        Artifact b3 = BuilderUtilTest.createBundle("group:someart:1.2.3", 4, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b3);
-        Artifact b0 = new Artifact(ArtifactId.fromMvnId("g:myart:1"));
+        Artifact b0 = BuilderUtilTest.createBundle("g:myart:1", new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b0);
 
         equals(result, assembled);
@@ -476,19 +514,17 @@ public class FeatureBuilderTest {
 
         Feature result = new Feature(ArtifactId.parse("g:tgtart:1"));
 
-        result.getBundles().add(BuilderUtilTest.createBundle("group/testmulti/2", 8));
+        result.getBundles().add(BuilderUtilTest.createBundle("group/testmulti/2", 8, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId())));
 
-        Artifact b1 = new Artifact(ArtifactId.fromMvnId("group:testmulti:1"));
-        b1.setStartOrder(4);
+        Artifact b1 = BuilderUtilTest.createBundle("group:testmulti:1",4, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b1);
 
-        Artifact b2 = new Artifact(ArtifactId.fromMvnId("group:testmulti:3"));
+        Artifact b2 = BuilderUtilTest.createBundle("group:testmulti:3", new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b2);
 
-        Artifact b3 = new Artifact(ArtifactId.fromMvnId("group:someart:1.2.3"));
-        b3.setStartOrder(4);
+        Artifact b3 = BuilderUtilTest.createBundle("group:someart:1.2.3", 4, new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b3);
-        Artifact b0 = new Artifact(ArtifactId.fromMvnId("g:myart:1"));
+        Artifact b0 = BuilderUtilTest.createBundle("g:myart:1", new AbstractMap.SimpleEntry("feature-origins", base.getId().toMvnId()));
         result.getBundles().add(b0);
 
         equals(result, assembled);