You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ge...@apache.org on 2018/06/15 08:45:50 UTC

[1/4] brooklyn-server git commit: does a yaml parse and compares the hash of the result when checking plan equivalence

Repository: brooklyn-server
Updated Branches:
  refs/heads/master baad0c039 -> e040f074f


does a yaml parse and compares the hash of the result when checking plan equivalence

means that the same plan entered twice but with different comments, spacing, quotations etc,
will not result in an error.  it only treats a plan as non-equivalent if their yaml is different
after a deserialize-then-serialize cycle.  previously a catalog push for an already-present
non-snapshot plan would fail unless the plan was identical up to a trim.  now it only fails if
the objects are different.


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/066cbbf2
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/066cbbf2
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/066cbbf2

Branch: refs/heads/master
Commit: 066cbbf2f70cb9b7e4b79a6eeb29bf87422dd2f6
Parents: 6f093c7
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Sat Jun 2 00:29:02 2018 +0100
Committer: Duncan Grant <du...@cloudsoftcorp.com>
Committed: Mon Jun 4 12:11:40 2018 +0100

----------------------------------------------------------------------
 .../brooklyn/core/typereg/RegisteredTypes.java  | 47 ++++++++++++++++++--
 1 file changed, 43 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/066cbbf2/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
index 9a4a5dd..83e7f05 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
@@ -50,6 +50,7 @@ import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.BrooklynTags.NamedStringTag;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.apache.brooklyn.core.typereg.JavaClassNameTypePlanTransformer.JavaClassNameTypeImplementationPlan;
+import org.apache.brooklyn.util.collections.Jsonya;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.guava.Maybe.Absent;
@@ -682,27 +683,65 @@ public class RegisteredTypes {
         // it does mean a format change will be ignored
         return "equivalent-plan("+Streams.getMd5Checksum(Streams.newInputStreamWithContents(input.trim()))+")";
     }
-    
+
+    /** parse the plan as yaml/json, re-serialize it, and take that checksum.
+     * this allows plans that are equivalent post-parse to be treated as equivalent.
+     * returns {@link Absent} if the input is not valid yaml.
+     */
+    private static Maybe<String> tagForEquivalentYamlPlan(String input) {
+        // plans may be trimmed by yaml parser so do that before checking equivalence
+        // it does mean a format change will be ignored
+        try {
+            Iterator<Object> plansI = Yamls.parseAll(input).iterator();
+            if (!plansI.hasNext()) {
+                return Maybe.absent("No data found");
+            }
+            String planOut = "";
+            while (plansI.hasNext()) {
+                Object plan = plansI.next();
+                if (!planOut.isEmpty()) planOut += "\n";
+                planOut += Jsonya.render(plan);
+            }
+            return Maybe.of(tagForEquivalentPlan(planOut));
+        } catch (Exception e) {
+            return Maybe.absent(e);
+        }
+    }
+
     @Beta
     public static void notePlanEquivalentToThis(RegisteredType type, TypeImplementationPlan plan) {
         Object data = plan.getPlanData();
         if (data==null) throw new IllegalStateException("No plan data for "+plan+" noted equivalent to "+type);
         if (!(data instanceof String)) throw new IllegalStateException("Expected plan for equivalent to "+type+" to be a string; was "+data);
         ((BasicRegisteredType)type).tags.add(tagForEquivalentPlan((String)data));
+        Maybe<String> reserializeEquivalenceTag = tagForEquivalentYamlPlan((String)data);
+        if (reserializeEquivalenceTag.isPresent()) ((BasicRegisteredType)type).tags.add(reserializeEquivalenceTag.get());
     }
 
+    /** Checks whether two types have plans which are identical, or identical after a YAML parse,
+     * or if either has an "equivalent-plan" tag indicating its equivalence to the other plan
+     * (as set by {@link #notePlanEquivalentToThis(RegisteredType, TypeImplementationPlan)}). 
+     */
     @Beta
     public static boolean arePlansEquivalent(RegisteredType type1, RegisteredType type2) {
         String plan1 = getImplementationDataStringForSpec(type1);
         String plan2 = getImplementationDataStringForSpec(type2);
         if (Strings.isNonBlank(plan1) && Strings.isNonBlank(plan2)) {
-            String p2tag = tagForEquivalentPlan(plan2);
             String p1tag = tagForEquivalentPlan(plan1);
-            // allow same plan under trimming,
-            // or any recorded tag in either direction
+            String p2tag = tagForEquivalentPlan(plan2);
+            
             if (Objects.equal(p1tag, p2tag)) return true;
+            
             if (type1.getTags().contains(p2tag)) return true;
             if (type2.getTags().contains(p1tag)) return true;
+            
+            Maybe<String> rp2tag = tagForEquivalentYamlPlan(plan2);
+            if (rp2tag.isPresent() && type1.getTags().contains(rp2tag.get())) return true;
+            
+            Maybe<String> rp1tag = tagForEquivalentYamlPlan(plan1);
+            if (rp1tag.isPresent() && type2.getTags().contains(rp1tag.get())) return true;
+            
+            if (rp1tag.isPresent() && rp2tag.isPresent() && rp1tag.get().equals(rp2tag.get())) return true; 
         }
         return Objects.equal(type1.getPlan(), type2.getPlan());
     }


[3/4] brooklyn-server git commit: Refactor tests to be more readable

Posted by ge...@apache.org.
Refactor tests to be more readable


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/1962b04e
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/1962b04e
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/1962b04e

Branch: refs/heads/master
Commit: 1962b04e539d08154000c70559213e8b54c7191d
Parents: c5e4e29
Author: Duncan Grant <du...@cloudsoftcorp.com>
Authored: Tue Jun 12 16:12:26 2018 +0100
Committer: Duncan Grant <du...@cloudsoftcorp.com>
Committed: Tue Jun 12 16:21:35 2018 +0100

----------------------------------------------------------------------
 .../core/typereg/RegisteredTypesTest.java       | 133 ++++++++++---------
 1 file changed, 67 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1962b04e/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypesTest.java b/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypesTest.java
index 7162f61..ae137c2 100644
--- a/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypesTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypesTest.java
@@ -1,7 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
 package org.apache.brooklyn.core.typereg;
 
 import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry;
 import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.util.text.Strings;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import javax.annotation.Nullable;
@@ -10,94 +30,75 @@ import static org.testng.Assert.*;
 
 public class RegisteredTypesTest {
 
-    public static final String DRBD_YAML = "brooklyn.catalog:\n" +
-            "  version: \"1.0.0-SNAPSHOT\" # BROOKLYN_DRBD_VERSION\n" +
-            "  publish:\n" +
-            "    description: |\n" +
-            "      DRBD is a distributed replicated storage system for the Linux platform. It is implemented as a kernel driver, \n" +
-            "      several userspace management applications, and some shell scripts. DRBD is traditionally used in high availability (HA) computer clusters to provide a\n" +
-            "      RAID 1 like configuration over a network.\n" +
-            "    license_code: Apache-2.0\n" +
-            "    defaults:\n" +
-            "      drbdIconUrl: \"https://s3.eu-central-1.amazonaws.com/misc-csft/drbd.jpg\"\n" +
-            "      classpathIconUrl: &drbdIconUrl 'classpath://io.brooklyn.drbd.brooklyn-drbd:drbd.png'\n" +
-            "  items:\n" +
-            "  - \"https://github.com/brooklyncentral/common-catalog-utils/releases/download/v0.1.0/common.bom\"\n" +
-            "  - id: drbd-node\n";
-
-    public static final String DRBD_TESTS_YAML = "brooklyn.catalog:\n" +
-            "  version: \"1.0.0-SNAPSHOT\" # BROOKLYN_DRBD_VERSION\n" +
-            "  items:\n" +
-            "  - https://github.com/brooklyncentral/common-catalog-utils/releases/download/v0.1.0/common.tests.bom\n" +
-            "  - id: drbd-test\n" +
-            "    name: DRBD Test\n" +
-            "    itemType: template\n" +
-            "    iconUrl: https://s3.eu-central-1.amazonaws.com/misc-csft/drbd.jpg\n" +
-            "    license: Apache-2.0\n" +
-            "    item:\n" +
-            "      booklyn.config:\n" +
-            "        defaultDisplayName: DRBD Test\n" +
-            "      services:\n" +
-            "      - type: drbd-webapp\n";
-
-    public static final String DRBR_YAML_WITH_COMMENTS = DRBD_YAML + "\n" +
-            "# this is a comment";
-    private static final String DRBR_YAML_WITHOUT_QUOTES = "brooklyn.catalog:\n" +
-            "  version: 1.0.0-SNAPSHOT # BROOKLYN_DRBD_VERSION\n" +
-            "  publish:\n" +
-            "    description: |\n" +
-            "      DRBD is a distributed replicated storage system for the Linux platform. It is implemented as a kernel driver, \n" +
-            "      several userspace management applications, and some shell scripts. DRBD is traditionally used in high availability (HA) computer clusters to provide a\n" +
-            "      RAID 1 like configuration over a network.\n" +
-            "    license_code: Apache-2.0\n" +
-            "    defaults:\n" +
-            "      drbdIconUrl: https://s3.eu-central-1.amazonaws.com/misc-csft/drbd.jpg\n" +
-            "      classpathIconUrl: &drbdIconUrl 'classpath://io.brooklyn.drbd.brooklyn-drbd:drbd.png'\n" +
-            "  items:\n" +
-            "  - https://github.com/brooklyncentral/common-catalog-utils/releases/download/v0.1.0/common.bom\n" +
-            "  - id: drbd-node\n";
+    private static final String DRBD_YAML = Strings.lines(
+            "brooklyn.catalog:",
+            "  version: \"1.0.0-SNAPSHOT\" # BROOKLYN_DRBD_VERSION",
+            "  items:",
+            "  - \"https://github.com/brooklyncentral/common-catalog-utils/releases/download/v0.1.0/common.bom\"",
+            "  - id: drbd-node");
+
+    private static final String DRBD_TESTS_YAML = Strings.lines(
+            "brooklyn.catalog:",
+            "  version: \"1.0.0-SNAPSHOT\" # BROOKLYN_DRBD_VERSION",
+            "  items:",
+            "  - https://github.com/brooklyncentral/common-catalog-utils/releases/download/v0.1.0/common.tests.bom\n");
+
+    private static final String DRBR_YAML_WITH_COMMENTS = Strings.lines(
+            DRBD_YAML,
+            "# this is a comment");
+
+    private static final String DRBR_YAML_WITHOUT_QUOTES = Strings.lines(
+            "brooklyn.catalog:",
+            "  version: 1.0.0-SNAPSHOT # BROOKLYN_DRBD_VERSION",
+            "  items:",
+            "  - https://github.com/brooklyncentral/common-catalog-utils/releases/download/v0.1.0/common.bom",
+            "  - id: drbd-node");
 
     @Test
     public void testIdenticalPlansAreEquivalent() {
-        BasicRegisteredType a = makeTypeWithYaml(DRBD_YAML);
+        BasicRegisteredType planA = makeTypeWithYaml(DRBD_YAML);
 
-        BasicRegisteredType b = makeTypeWithYaml(DRBD_YAML);
+        BasicRegisteredType planB = makeTypeWithYaml(DRBD_YAML);
 
-        boolean plansEquivalent = RegisteredTypes.arePlansEquivalent(
-                a, b);
-        assertTrue(plansEquivalent);
+        assertPlansEquivalent(planA, planB, "Identical plans");
     }
 
     @Test
     public void testDifferentPlansAreNotEquivalent() {
-        BasicRegisteredType a = makeTypeWithYaml(DRBD_YAML);
-        BasicRegisteredType b = makeTypeWithYaml(DRBD_TESTS_YAML);
+        BasicRegisteredType planA = makeTypeWithYaml(DRBD_YAML);
+        BasicRegisteredType planB = makeTypeWithYaml(DRBD_TESTS_YAML);
 
-        boolean plansEquivalent = RegisteredTypes.arePlansEquivalent(
-                a, b);
-        assertFalse(plansEquivalent);
+        assertPlansDifferent(planA, planB, "Completely different plans");
     }
 
     @Test
     public void testComparisonIgnoresComments() {
-        BasicRegisteredType a = makeTypeWithYaml(DRBD_YAML);
+        BasicRegisteredType planA = makeTypeWithYaml(DRBD_YAML);
 
-        BasicRegisteredType b = makeTypeWithYaml(DRBR_YAML_WITH_COMMENTS);
+        BasicRegisteredType planB = makeTypeWithYaml(DRBR_YAML_WITH_COMMENTS);
 
-        boolean plansEquivalent = RegisteredTypes.arePlansEquivalent(
-                a, b);
-        assertTrue(plansEquivalent);
+        assertPlansEquivalent(planA, planB, "Only difference comments");
     }
 
     @Test
     public void testComparisonIgnoresQuotedStrings() {
-        BasicRegisteredType a = makeTypeWithYaml(DRBD_YAML);
+        BasicRegisteredType planA = makeTypeWithYaml(DRBD_YAML);
 
-        BasicRegisteredType b = makeTypeWithYaml(DRBR_YAML_WITHOUT_QUOTES);
+        BasicRegisteredType planB = makeTypeWithYaml(DRBR_YAML_WITHOUT_QUOTES);
 
-        boolean plansEquivalent = RegisteredTypes.arePlansEquivalent(
-                a, b);
-        assertTrue(plansEquivalent);
+        assertPlansEquivalent(planA, planB, "Compares same yaml with and without quotes");
+    }
+
+    private void assertPlansDifferent(BasicRegisteredType planA, BasicRegisteredType planB, String messageOnError) {
+        assertFalse(RegisteredTypes.arePlansEquivalent(planA, planB), createErrorMessage("Plans equivalent: ", planA, planB, messageOnError));
+    }
+
+    private void assertPlansEquivalent(BasicRegisteredType planA, BasicRegisteredType planB, String messageOnError) {
+        assertTrue(RegisteredTypes.arePlansEquivalent(planA, planB), createErrorMessage("Plans differ: ",planA, planB, messageOnError));
+    }
+
+    private String createErrorMessage(String issue, BasicRegisteredType planA, BasicRegisteredType planB, String messageOnError) {
+        return Strings.lines(issue + messageOnError, planA.toString(), planB.toString());
     }
 
     BasicRegisteredType makeTypeWithYaml(final String yaml) {


[4/4] brooklyn-server git commit: Closes #969 Closes #968

Posted by ge...@apache.org.
Closes #969
Closes #968

Add registered types test

This PR follows on from #968
It adds 2 commits.  The first adds tests for the functionality fixed in #968
The second changes the logic in #968 in a way that I think is equivalent but I haven't managed to add the requisite test coverage to prove this.


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/e040f074
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/e040f074
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/e040f074

Branch: refs/heads/master
Commit: e040f074f71ef12c4f0798ea6f28850ddc1c0f81
Parents: baad0c0 1962b04
Author: Geoff Macartney <ge...@cloudsoftcorp.com>
Authored: Fri Jun 15 09:44:39 2018 +0100
Committer: Geoff Macartney <ge...@cloudsoftcorp.com>
Committed: Fri Jun 15 09:45:21 2018 +0100

----------------------------------------------------------------------
 .../brooklyn/core/typereg/RegisteredTypes.java  |  49 +++++++-
 .../core/typereg/RegisteredTypesTest.java       | 124 +++++++++++++++++++
 2 files changed, 169 insertions(+), 4 deletions(-)
----------------------------------------------------------------------



[2/4] brooklyn-server git commit: Add tests to RegisteredTypes.arePlansEquivilant

Posted by ge...@apache.org.
Add tests to RegisteredTypes.arePlansEquivilant

Hopefully these show that we now ignore things like comments and strings
with surrounding quotes


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/c5e4e299
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/c5e4e299
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/c5e4e299

Branch: refs/heads/master
Commit: c5e4e2993e8f2448286cbf08295cd7eebb006daf
Parents: 066cbbf
Author: Duncan Grant <du...@cloudsoftcorp.com>
Authored: Tue Jun 5 16:24:23 2018 +0100
Committer: Duncan Grant <du...@cloudsoftcorp.com>
Committed: Tue Jun 5 16:24:23 2018 +0100

----------------------------------------------------------------------
 .../brooklyn/core/typereg/RegisteredTypes.java  |   2 +
 .../core/typereg/RegisteredTypesTest.java       | 123 +++++++++++++++++++
 2 files changed, 125 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c5e4e299/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
index 83e7f05..f76fcb1 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
@@ -25,10 +25,12 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.StreamSupport;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import com.google.common.collect.Iterators;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
 import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c5e4e299/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypesTest.java b/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypesTest.java
new file mode 100644
index 0000000..7162f61
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypesTest.java
@@ -0,0 +1,123 @@
+package org.apache.brooklyn.core.typereg;
+
+import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry;
+import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.testng.annotations.Test;
+
+import javax.annotation.Nullable;
+
+import static org.testng.Assert.*;
+
+public class RegisteredTypesTest {
+
+    public static final String DRBD_YAML = "brooklyn.catalog:\n" +
+            "  version: \"1.0.0-SNAPSHOT\" # BROOKLYN_DRBD_VERSION\n" +
+            "  publish:\n" +
+            "    description: |\n" +
+            "      DRBD is a distributed replicated storage system for the Linux platform. It is implemented as a kernel driver, \n" +
+            "      several userspace management applications, and some shell scripts. DRBD is traditionally used in high availability (HA) computer clusters to provide a\n" +
+            "      RAID 1 like configuration over a network.\n" +
+            "    license_code: Apache-2.0\n" +
+            "    defaults:\n" +
+            "      drbdIconUrl: \"https://s3.eu-central-1.amazonaws.com/misc-csft/drbd.jpg\"\n" +
+            "      classpathIconUrl: &drbdIconUrl 'classpath://io.brooklyn.drbd.brooklyn-drbd:drbd.png'\n" +
+            "  items:\n" +
+            "  - \"https://github.com/brooklyncentral/common-catalog-utils/releases/download/v0.1.0/common.bom\"\n" +
+            "  - id: drbd-node\n";
+
+    public static final String DRBD_TESTS_YAML = "brooklyn.catalog:\n" +
+            "  version: \"1.0.0-SNAPSHOT\" # BROOKLYN_DRBD_VERSION\n" +
+            "  items:\n" +
+            "  - https://github.com/brooklyncentral/common-catalog-utils/releases/download/v0.1.0/common.tests.bom\n" +
+            "  - id: drbd-test\n" +
+            "    name: DRBD Test\n" +
+            "    itemType: template\n" +
+            "    iconUrl: https://s3.eu-central-1.amazonaws.com/misc-csft/drbd.jpg\n" +
+            "    license: Apache-2.0\n" +
+            "    item:\n" +
+            "      booklyn.config:\n" +
+            "        defaultDisplayName: DRBD Test\n" +
+            "      services:\n" +
+            "      - type: drbd-webapp\n";
+
+    public static final String DRBR_YAML_WITH_COMMENTS = DRBD_YAML + "\n" +
+            "# this is a comment";
+    private static final String DRBR_YAML_WITHOUT_QUOTES = "brooklyn.catalog:\n" +
+            "  version: 1.0.0-SNAPSHOT # BROOKLYN_DRBD_VERSION\n" +
+            "  publish:\n" +
+            "    description: |\n" +
+            "      DRBD is a distributed replicated storage system for the Linux platform. It is implemented as a kernel driver, \n" +
+            "      several userspace management applications, and some shell scripts. DRBD is traditionally used in high availability (HA) computer clusters to provide a\n" +
+            "      RAID 1 like configuration over a network.\n" +
+            "    license_code: Apache-2.0\n" +
+            "    defaults:\n" +
+            "      drbdIconUrl: https://s3.eu-central-1.amazonaws.com/misc-csft/drbd.jpg\n" +
+            "      classpathIconUrl: &drbdIconUrl 'classpath://io.brooklyn.drbd.brooklyn-drbd:drbd.png'\n" +
+            "  items:\n" +
+            "  - https://github.com/brooklyncentral/common-catalog-utils/releases/download/v0.1.0/common.bom\n" +
+            "  - id: drbd-node\n";
+
+    @Test
+    public void testIdenticalPlansAreEquivalent() {
+        BasicRegisteredType a = makeTypeWithYaml(DRBD_YAML);
+
+        BasicRegisteredType b = makeTypeWithYaml(DRBD_YAML);
+
+        boolean plansEquivalent = RegisteredTypes.arePlansEquivalent(
+                a, b);
+        assertTrue(plansEquivalent);
+    }
+
+    @Test
+    public void testDifferentPlansAreNotEquivalent() {
+        BasicRegisteredType a = makeTypeWithYaml(DRBD_YAML);
+        BasicRegisteredType b = makeTypeWithYaml(DRBD_TESTS_YAML);
+
+        boolean plansEquivalent = RegisteredTypes.arePlansEquivalent(
+                a, b);
+        assertFalse(plansEquivalent);
+    }
+
+    @Test
+    public void testComparisonIgnoresComments() {
+        BasicRegisteredType a = makeTypeWithYaml(DRBD_YAML);
+
+        BasicRegisteredType b = makeTypeWithYaml(DRBR_YAML_WITH_COMMENTS);
+
+        boolean plansEquivalent = RegisteredTypes.arePlansEquivalent(
+                a, b);
+        assertTrue(plansEquivalent);
+    }
+
+    @Test
+    public void testComparisonIgnoresQuotedStrings() {
+        BasicRegisteredType a = makeTypeWithYaml(DRBD_YAML);
+
+        BasicRegisteredType b = makeTypeWithYaml(DRBR_YAML_WITHOUT_QUOTES);
+
+        boolean plansEquivalent = RegisteredTypes.arePlansEquivalent(
+                a, b);
+        assertTrue(plansEquivalent);
+    }
+
+    BasicRegisteredType makeTypeWithYaml(final String yaml) {
+        return new BasicRegisteredType(
+                BrooklynTypeRegistry.RegisteredTypeKind.SPEC,
+                "B",
+                "1",
+                new RegisteredType.TypeImplementationPlan() {
+                    @Nullable
+                    @Override
+                    public String getPlanFormat() {
+                        return null;
+                    }
+
+                    @Override
+                    public Object getPlanData() {
+                        return yaml;
+                    }
+                });
+    }
+
+
+}
\ No newline at end of file