Add workflow for ActionController
ActionController's workflow gets split into three components:
* PropertyExtractor (extracts properties from an event),
* RuleBase (assigns actions to properties), and
* ActionExecutor (executes matched actions).
Change-Id: Ideb81b242a0f097c94c76bd90afff4bdb438609a
diff --git a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/ItsHookModule.java b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/ItsHookModule.java
index 86097ef..de7709d 100644
--- a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/ItsHookModule.java
+++ b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/ItsHookModule.java
@@ -16,17 +16,19 @@
import com.google.gerrit.common.ChangeListener;
import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.git.validators.CommitValidationListener;
-import com.google.inject.AbstractModule;
import com.googlesource.gerrit.plugins.hooks.its.ItsName;
import com.googlesource.gerrit.plugins.hooks.validation.ItsValidateComment;
+import com.googlesource.gerrit.plugins.hooks.workflow.ActionRequest;
import com.googlesource.gerrit.plugins.hooks.workflow.GerritHookFilterAddComment;
import com.googlesource.gerrit.plugins.hooks.workflow.GerritHookFilterAddRelatedLinkToChangeId;
import com.googlesource.gerrit.plugins.hooks.workflow.GerritHookFilterAddRelatedLinkToGitWeb;
import com.googlesource.gerrit.plugins.hooks.workflow.GerritHookFilterChangeState;
import com.googlesource.gerrit.plugins.hooks.workflow.ActionController;
+import com.googlesource.gerrit.plugins.hooks.workflow.Property;
-public class ItsHookModule extends AbstractModule {
+public class ItsHookModule extends FactoryModule {
private String itsName;
@@ -49,5 +51,7 @@
ItsValidateComment.class);
DynamicSet.bind(binder(), ChangeListener.class).to(
ActionController.class);
+ factory(ActionRequest.Factory.class);
+ factory(Property.Factory.class);
}
}
diff --git a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/util/PropertyExtractor.java b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/util/PropertyExtractor.java
new file mode 100644
index 0000000..9cf514e
--- /dev/null
+++ b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/util/PropertyExtractor.java
@@ -0,0 +1,73 @@
+//Copyright (C) 2013 The Android Open Source Project
+//
+//Licensed 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 com.googlesource.gerrit.plugins.hooks.util;
+
+import java.util.Set;
+
+import com.google.gerrit.server.events.ChangeEvent;
+
+import com.googlesource.gerrit.plugins.hooks.workflow.Property;
+
+/**
+ * Extractor to translate an {@link ChangeEvent} to
+ * {@link Property Properties}.
+ */
+public class PropertyExtractor {
+ /**
+ * A set of property sets extracted from an event.
+ *
+ * As events may relate to more that a single issue, and properties sets are
+ * should be tied to a single issue, returning {@code Collection<Property>}
+ * is not sufficient, and we need to return
+ * {@code Collection<Collection<Property>>}. Using this approach, a
+ * PatchSetCreatedEvent for a patch set with commit message:
+ *
+ * <pre>
+ * (bug 4711) Fix treatment of special characters in title
+ *
+ * This commit mitigates the effects of bug 42, but does not fix them.
+ *
+ * Change-Id: I1234567891123456789212345678931234567894
+ * </pre>
+ *
+ * may return both
+ *
+ * <pre>
+ * issue: 4711
+ * association: subject
+ * event: PatchSetCreatedEvent
+ * </pre>
+ *
+ * and
+ *
+ * <pre>
+ * issue: 42
+ * association: body
+ * event: PatchSetCreatedEvent
+ * </pre>
+ *
+ * Thereby, sites can choose to to cause different actions for different
+ * issues associated to the same event. So in the above example, a comment
+ * "mentioned in change 123" may be added for issue 42, and a comment
+ * "fixed by change 123” may be added for issue 4711.
+ *
+ * @param event The event to extract property sets from.
+ * @return sets of property sets extracted from the event.
+ */
+ public Set<Set<Property>> extractFrom(ChangeEvent event) {
+ // TODO implement
+ throw new RuntimeException("unimplemented");
+ }
+}
diff --git a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionController.java b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionController.java
index 9d6ad99..e5c4728 100644
--- a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionController.java
+++ b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionController.java
@@ -14,19 +14,48 @@
package com.googlesource.gerrit.plugins.hooks.workflow;
+import java.util.Collection;
+import java.util.Set;
+
import com.google.gerrit.common.ChangeListener;
import com.google.gerrit.server.events.ChangeEvent;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.hooks.util.PropertyExtractor;
+/**
+ * Controller that takes actions according to {@code ChangeEvents@}.
+ *
+ * The taken actions are typically Its related (e.g.: adding an Its comment, or
+ * changing an issue's status).
+ */
public class ActionController implements ChangeListener {
- public ActionController() {
- // TODO construct rule base
+ private final PropertyExtractor propertyExtractor;
+ private final RuleBase ruleBase;
+ private final ActionExecutor actionExecutor;
+
+ @Inject
+ public ActionController(PropertyExtractor propertyExtractor,
+ RuleBase ruleBase, ActionExecutor actionExecutor) {
+ this.propertyExtractor = propertyExtractor;
+ this.ruleBase = ruleBase;
+ this.actionExecutor = actionExecutor;
}
@Override
public void onChangeEvent(ChangeEvent event) {
- // TODO extract conditions from event
- // TODO find rules in rule base that match the extracted conditions
- // TODO fire actions for matched rules
+ Set<Set<Property>> propertiesCollections =
+ propertyExtractor.extractFrom(event);
+ for (Set<Property> properties : propertiesCollections) {
+ Collection<ActionRequest> actions =
+ ruleBase.actionRequestsFor(properties);
+ if (!actions.isEmpty()) {
+ for (Property property : properties) {
+ if ("issue".equals(property.getKey())) {
+ String issue = property.getValue();
+ actionExecutor.execute(issue, actions);
+ }
+ }
+ }
+ }
}
-
}
diff --git a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionExecutor.java b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionExecutor.java
new file mode 100644
index 0000000..13ab6de
--- /dev/null
+++ b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionExecutor.java
@@ -0,0 +1,25 @@
+//Copyright (C) 2013 The Android Open Source Project
+//
+//Licensed 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 com.googlesource.gerrit.plugins.hooks.workflow;
+
+/**
+ * Executes an {@link ActionRequest}
+ */
+public class ActionExecutor {
+ public void execute(String issue, Iterable<ActionRequest> actions) {
+ // TODO implement
+ throw new RuntimeException("unimplemented");
+ }
+}
diff --git a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionRequest.java b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionRequest.java
new file mode 100644
index 0000000..bbf2690
--- /dev/null
+++ b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionRequest.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.hooks.workflow;
+
+import javax.annotation.Nullable;
+
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+/**
+ * An action to take for an {@code ChangeEvent}.
+ *
+ * Actions are typically related to an Its (e.g.:adding an Its comment, or
+ * changing an issue's status).
+ */
+public class ActionRequest {
+
+ public interface Factory {
+ ActionRequest create(String specification);
+ }
+
+ @Inject
+ public ActionRequest(@Nullable @Assisted String specification) {
+ }
+}
diff --git a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/Property.java b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/Property.java
new file mode 100644
index 0000000..e8cc634
--- /dev/null
+++ b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/Property.java
@@ -0,0 +1,83 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.hooks.workflow;
+
+import javax.annotation.Nullable;
+
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+/**
+ * A property to match against {@code Condition}s.
+ *
+ * A property is a simple key value pair.
+ */
+public class Property {
+ public interface Factory {
+ Property create(@Assisted("key") String key,
+ @Assisted("value") String value);
+ }
+
+ private final String key;
+ private final String value;
+
+ @Inject
+ public Property(@Assisted("key") String key,
+ @Nullable @Assisted("value") String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ boolean ret = false;
+ if (other != null && other instanceof Property) {
+ Property otherProperty = (Property) other;
+ ret = true;
+
+ if (key == null) {
+ ret &= otherProperty.getKey() == null;
+ } else {
+ ret &= key.equals(otherProperty.getKey());
+ }
+
+ if (value == null) {
+ ret &= otherProperty.getValue() == null;
+ } else {
+ ret &= value.equals(otherProperty.getValue());
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public int hashCode() {
+ return (key == null ? 0 : key.hashCode()) * 31 +
+ (value == null ? 0 : value.hashCode());
+ }
+
+ @Override
+ public String toString() {
+ return "[" + key + " = " + value + "]";
+ }
+}
diff --git a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/RuleBase.java b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/RuleBase.java
new file mode 100644
index 0000000..e7ef399
--- /dev/null
+++ b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/workflow/RuleBase.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.hooks.workflow;
+
+import java.util.Collection;
+
+/**
+ * Collection and matcher agains {@link Rule}s.
+ */
+public class RuleBase {
+ public RuleBase() {
+ // TODO construct rule base
+ }
+
+ /**
+ * Gets the action requests for a set of properties.
+ *
+ * @param properties The properties to search actions for.
+ * @return Requests for the actions that should be fired.
+ */
+ public Collection<ActionRequest> actionRequestsFor(
+ Iterable<Property> properties) {
+ // TODO implement
+ throw new RuntimeException("unimplemented");
+ }
+}
diff --git a/hooks-its/src/test/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionControllerTest.java b/hooks-its/src/test/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionControllerTest.java
new file mode 100644
index 0000000..926fe8b
--- /dev/null
+++ b/hooks-its/src/test/java/com/googlesource/gerrit/plugins/hooks/workflow/ActionControllerTest.java
@@ -0,0 +1,200 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.hooks.workflow;
+
+import static org.easymock.EasyMock.expect;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.server.events.ChangeEvent;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.hooks.testutil.LoggingMockingTestCase;
+import com.googlesource.gerrit.plugins.hooks.util.PropertyExtractor;
+
+public class ActionControllerTest extends LoggingMockingTestCase {
+ private Injector injector;
+
+ private PropertyExtractor propertyExtractor;
+ private RuleBase ruleBase;
+ private ActionExecutor actionExecutor;
+
+ public void testNoPropertySets() {
+ ActionController actionController = createActionController();
+
+ ChangeEvent event = createMock(ChangeEvent.class);
+
+ Set<Set<Property>> propertySets = Collections.emptySet();
+ expect(propertyExtractor.extractFrom(event)).andReturn(propertySets)
+ .anyTimes();
+
+ replayMocks();
+
+ actionController.onChangeEvent(event);
+ }
+
+ public void testNoActions() {
+ ActionController actionController = createActionController();
+
+ ChangeEvent event = createMock(ChangeEvent.class);
+
+ Set<Set<Property>> propertySets = Sets.newHashSet();
+ Set<Property> propertySet = Collections.emptySet();
+ propertySets.add(propertySet);
+
+ expect(propertyExtractor.extractFrom(event)).andReturn(propertySets)
+ .anyTimes();
+
+ Collection<ActionRequest> actions = Collections.emptySet();
+ expect(ruleBase.actionRequestsFor(propertySet)).andReturn(actions).once();
+
+ replayMocks();
+
+ actionController.onChangeEvent(event);
+ }
+
+ public void testNoIssues() {
+ ActionController actionController = createActionController();
+
+ ChangeEvent event = createMock(ChangeEvent.class);
+
+ Set<Set<Property>> propertySets = Sets.newHashSet();
+ Set<Property> propertySet = Collections.emptySet();
+ propertySets.add(propertySet);
+
+ expect(propertyExtractor.extractFrom(event)).andReturn(propertySets)
+ .anyTimes();
+
+ Collection<ActionRequest> actions = Lists.newArrayListWithCapacity(1);
+ ActionRequest action1 = createMock(ActionRequest.class);
+ actions.add(action1);
+ expect(ruleBase.actionRequestsFor(propertySet)).andReturn(actions).once();
+
+ replayMocks();
+
+ actionController.onChangeEvent(event);
+ }
+
+ public void testSinglePropertySetSingleActionSingleIssue() {
+ ActionController actionController = createActionController();
+
+ ChangeEvent event = createMock(ChangeEvent.class);
+
+ Property propertyIssue1 = createMock(Property.class);
+ expect(propertyIssue1.getKey()).andReturn("issue").anyTimes();
+ expect(propertyIssue1.getValue()).andReturn("testIssue").anyTimes();
+
+ Set<Property> propertySet = Sets.newHashSet();
+ propertySet.add(propertyIssue1);
+
+ Set<Set<Property>> propertySets = Sets.newHashSet();
+ propertySets.add(propertySet);
+
+ expect(propertyExtractor.extractFrom(event)).andReturn(propertySets)
+ .anyTimes();
+
+ Collection<ActionRequest> actionRequests =
+ Lists.newArrayListWithCapacity(1);
+ ActionRequest actionRequest1 = createMock(ActionRequest.class);
+ actionRequests.add(actionRequest1);
+ expect(ruleBase.actionRequestsFor(propertySet)).andReturn(actionRequests)
+ .once();
+
+ actionExecutor.execute("testIssue", actionRequests);
+
+ replayMocks();
+
+ actionController.onChangeEvent(event);
+ }
+
+ public void testMultiplePropertySetsMultipleActionMultipleIssue() {
+ ActionController actionController = createActionController();
+
+ ChangeEvent event = createMock(ChangeEvent.class);
+
+ Property propertyIssue1 = createMock(Property.class);
+ expect(propertyIssue1.getKey()).andReturn("issue").anyTimes();
+ expect(propertyIssue1.getValue()).andReturn("testIssue").anyTimes();
+
+ Property propertyIssue2 = createMock(Property.class);
+ expect(propertyIssue2.getKey()).andReturn("issue").anyTimes();
+ expect(propertyIssue2.getValue()).andReturn("testIssue2").anyTimes();
+
+ Set<Property> propertySet1 = Sets.newHashSet();
+ propertySet1.add(propertyIssue1);
+
+ Set<Property> propertySet2 = Sets.newHashSet();
+ propertySet2.add(propertyIssue1);
+ propertySet2.add(propertyIssue2);
+
+ Set<Set<Property>> propertySets = Sets.newHashSet();
+ propertySets.add(propertySet1);
+ propertySets.add(propertySet2);
+
+ expect(propertyExtractor.extractFrom(event)).andReturn(propertySets)
+ .anyTimes();
+
+ Collection<ActionRequest> actionRequests1 =
+ Lists.newArrayListWithCapacity(1);
+ ActionRequest actionRequest1 = createMock(ActionRequest.class);
+ actionRequests1.add(actionRequest1);
+
+ Collection<ActionRequest> actionRequests2 =
+ Lists.newArrayListWithCapacity(2);
+ ActionRequest actionRequest2 = createMock(ActionRequest.class);
+ actionRequests2.add(actionRequest2);
+ ActionRequest actionRequest3 = createMock(ActionRequest.class);
+ actionRequests2.add(actionRequest3);
+
+ expect(ruleBase.actionRequestsFor(propertySet1)).andReturn(actionRequests1)
+ .once();
+ expect(ruleBase.actionRequestsFor(propertySet2)).andReturn(actionRequests2)
+ .once();
+
+ actionExecutor.execute("testIssue", actionRequests1);
+ actionExecutor.execute("testIssue", actionRequests2);
+ actionExecutor.execute("testIssue2", actionRequests2);
+
+ replayMocks();
+
+ actionController.onChangeEvent(event);
+ }
+ private ActionController createActionController() {
+ return injector.getInstance(ActionController.class);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ propertyExtractor = createMock(PropertyExtractor.class);
+ bind(PropertyExtractor.class).toInstance(propertyExtractor);
+
+ ruleBase = createMock(RuleBase.class);
+ bind(RuleBase.class).toInstance(ruleBase);
+
+ actionExecutor = createMock(ActionExecutor.class);
+ bind(ActionExecutor.class).toInstance(actionExecutor);
+ }
+ }
+}
\ No newline at end of file
diff --git a/hooks-its/src/test/java/com/googlesource/gerrit/plugins/hooks/workflow/PropertyTest.java b/hooks-its/src/test/java/com/googlesource/gerrit/plugins/hooks/workflow/PropertyTest.java
new file mode 100644
index 0000000..c977208
--- /dev/null
+++ b/hooks-its/src/test/java/com/googlesource/gerrit/plugins/hooks/workflow/PropertyTest.java
@@ -0,0 +1,127 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.hooks.workflow;
+
+import com.google.gerrit.server.config.FactoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.hooks.testutil.LoggingMockingTestCase;
+
+public class PropertyTest extends LoggingMockingTestCase {
+ private Injector injector;
+
+ public void testGetKeyNull() {
+ Property property = new Property(null, "testValue");
+ assertNull("Key is not null", property.getKey());
+ }
+
+ public void testGetKeyNonNull() {
+ Property property = createProperty("testKey", "testValue");
+ assertEquals("Key does not match", "testKey", property.getKey());
+ }
+
+ public void testGetValueNull() {
+ Property property = createProperty("testKey", null);
+ assertNull("Value is not null", property.getValue());
+ }
+
+ public void testGetValueNonNull() {
+ Property property = createProperty("testKey", "testValue");
+ assertEquals("Value does not match", "testValue", property.getValue());
+ }
+
+ public void testEqualsSelf() {
+ Property property = createProperty("testKey", "testValue");
+ assertTrue("Property not equal to itself", property.equals(property));
+ }
+
+ public void testEqualsSimilar() {
+ Property propertyA = createProperty("testKey", "testValue");
+ Property propertyB = createProperty("testKey", "testValue");
+ assertTrue("Property is equal to similar", propertyA.equals(propertyB));
+ }
+
+ public void testEqualsNull() {
+ Property property = createProperty("testKey", "testValue");
+ assertFalse("Property is equal to null", property.equals(null));
+ }
+
+ public void testEqualsNull2() {
+ Property property = new Property(null, null);
+ assertFalse("Property is equal to null", property.equals(null));
+ }
+
+ public void testEqualsNulledKey() {
+ Property propertyA = new Property(null, "testValue");
+ Property propertyB = createProperty("testKey", "testValue");
+ assertFalse("Single nulled key does match",
+ propertyA.equals(propertyB));
+ }
+
+ public void testEqualsNulledKey2() {
+ Property propertyA = createProperty("testKey", "testValue");
+ Property propertyB = new Property(null, "testValue");
+ assertFalse("Single nulled key does match",
+ propertyA.equals(propertyB));
+ }
+
+ public void testEqualsNulledValue() {
+ Property propertyA = createProperty("testKey", "testValue");
+ Property propertyB = createProperty("testKey", null);
+ assertFalse("Single nulled value does match",
+ propertyA.equals(propertyB));
+ }
+
+ public void testEqualsNulledValue2() {
+ Property propertyA = createProperty("testKey", null);
+ Property propertyB = createProperty("testKey", "testValue");
+ assertFalse("Single nulled value does match",
+ propertyA.equals(propertyB));
+ }
+
+ public void testHashCodeEquals() {
+ Property propertyA = createProperty("testKey", "testValue");
+ Property propertyB = createProperty("testKey", "testValue");
+ assertEquals("Hash codes do not match", propertyA.hashCode(),
+ propertyB.hashCode());
+ }
+
+ public void testHashCodeNullKey() {
+ Property property = new Property(null, "testValue");
+ property.hashCode();
+ }
+
+ public void testHashCodeNullValue() {
+ Property property = createProperty("testKey", null);
+ property.hashCode();
+ }
+
+ private Property createProperty(String key, String value) {
+ Property.Factory factory = injector.getInstance(Property.Factory.class);
+ return factory.create(key, value);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ factory(Property.Factory.class);
+ }
+ }
+}
\ No newline at end of file