// Copyright 2017 Google Inc. All Rights Reserved. // // 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.google.api.ads.adwords.axis.utils.v201702.shopping; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import com.google.api.ads.adwords.axis.v201702.cm.ProductBiddingCategory; import com.google.api.ads.adwords.axis.v201702.cm.ProductBrand; import com.google.api.ads.adwords.axis.v201702.cm.ProductCanonicalCondition; import com.google.api.ads.adwords.axis.v201702.cm.ProductCanonicalConditionCondition; import com.google.api.ads.adwords.axis.v201702.cm.ProductChannel; import com.google.api.ads.adwords.axis.v201702.cm.ProductChannelExclusivity; import com.google.api.ads.adwords.axis.v201702.cm.ProductCustomAttribute; import com.google.api.ads.adwords.axis.v201702.cm.ProductDimension; import com.google.api.ads.adwords.axis.v201702.cm.ProductDimensionType; import com.google.api.ads.adwords.axis.v201702.cm.ProductOfferId; import com.google.api.ads.adwords.axis.v201702.cm.ProductType; import com.google.api.ads.adwords.axis.v201702.cm.ShoppingProductChannel; import com.google.api.ads.adwords.axis.v201702.cm.ShoppingProductChannelExclusivity; import com.google.common.collect.Maps; import java.util.Map; import java.util.Set; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for {@link ProductPartitionNode}. */ @RunWith(JUnit4.class) public class ProductPartitionNodeTest { private ProductPartitionNode rootNode; @Rule public ExpectedException thrown = ExpectedException.none(); @Before public void setUp() { rootNode = new ProductPartitionNode(null, null, -1L, new ProductDimensionComparator()); } @Test public void testRootNodeBasicFunctionality() { assertNull("parent of parentNode should be null", rootNode.getParent()); assertNull("parentNode dimension should be null", rootNode.getDimension()); assertEquals("dimension from getDimension should be logically equivalent to null", 0, new ProductDimensionComparator().compare(null, rootNode.getDimension())); assertEquals("partition ID is incorrect after constructor", -1L, rootNode.getProductPartitionId().longValue()); rootNode = rootNode.setProductPartitionId(-2L); assertEquals("partition ID is incorrect after setProductPartitionId", -2L, rootNode.getProductPartitionId().longValue()); assertFalse("parentNode should not have any children", rootNode.getChildren().iterator().hasNext()); assertTrue("new node should be a unit node by default", rootNode.isUnit()); assertFalse("new node should not be a subdivision by default", rootNode.isSubdivision()); assertTrue("new node should be a biddable unit node by default", rootNode.isBiddableUnit()); } @Test public void testChildNodeBasicFunctionality() { rootNode = rootNode.asSubdivision(); assertFalse("parent should not be a unit", rootNode.isUnit()); assertTrue("parent should be a subdivision", rootNode.isSubdivision()); ProductBrand childDimension = ProductDimensions.createBrand("google"); ProductPartitionNode childNode = rootNode.addChild(childDimension); assertNotSame("getDimension should return a copy", childDimension, childNode.getDimension()); assertEquals("dimension from getDimension should be logically equivalent to childDimension", 0, new ProductDimensionComparator().compare(childDimension, childNode.getDimension())); assertSame("child.getParent should return parentNode", rootNode, childNode.getParent()); assertNull("partition ID is incorrect", childNode.getProductPartitionId()); assertFalse("childNode should not have any children", childNode.getChildren().iterator().hasNext()); assertTrue("new node should be a unit node by default", childNode.isUnit()); assertTrue("new node should be a biddable unit node by default", childNode.isBiddableUnit()); assertTrue("rootNode.hasChild should return true when passed the dimension of the added child", rootNode.hasChild(childDimension)); assertFalse("rootNode.hasChild should return false when passed a dimension for a nonexistent " + "child", rootNode.hasChild(ProductDimensions.createBrand("xyz"))); assertFalse("rootNode.hasChild should return false when passed a dimension for a nonexistent " + "child", rootNode.hasChild(null)); String childToString = childNode.toString(); assertNotNull("child toString returned null", childToString); String parentDetailedToString = rootNode.toDetailedString(); assertThat("Parent toDetailedString does not contain the child's toString", parentDetailedToString, Matchers.containsString(childToString)); String parentToString = rootNode.toString(); assertNotNull("parent toString returned null", parentToString); assertThat("Parent toDetailedString does not contain the parent's toString", parentDetailedToString, Matchers.containsString(parentToString)); } /** * Checks that adding a child to a UNIT node fails. */ @Test public void testAddChildToUnit_fails() { assertTrue("root should be a unit by default", rootNode.isUnit()); thrown.expect(IllegalArgumentException.class); rootNode.addChild(ProductDimensions.createBrand("google")); } @Test public void testCreateExcludedRoot_fails() { thrown.expect(IllegalStateException.class); rootNode.asExcludedUnit(); } @Test public void testGetChildThatDoesNotExist_fails() { rootNode = rootNode.asSubdivision(); thrown.expect(IllegalArgumentException.class); rootNode.getChild(ProductDimensions.createBrand("google")); } @Test public void testRemoveChildThatDoesNotExist_fails() { rootNode = rootNode.asSubdivision(); thrown.expect(IllegalArgumentException.class); rootNode.removeChild(ProductDimensions.createBrand("google")); } @Test public void testAddChildThatExists_fails() { rootNode = rootNode.asSubdivision(); rootNode.addChild(ProductDimensions.createBrand("google")); // Add the same child again. The call should fail. thrown.expect(IllegalArgumentException.class); rootNode.addChild(ProductDimensions.createBrand("google")); } /** * Checks that setting a bid on a SUBDIVISION node fails. */ @Test public void testSetBidOnSubdivision_fails() { rootNode = rootNode.asSubdivision(); thrown.expect(IllegalStateException.class); rootNode.setBid(1L); } /** * Checks that setting a negative bid on a UNIT node fails. */ @Test public void testSetNegativeBid_fails() { assertTrue("root should be a unit by default", rootNode.isUnit()); thrown.expect(IllegalArgumentException.class); rootNode.setBid(-1L); } @Test public void testSetBidOnUnit() { rootNode = rootNode.asSubdivision(); ProductBrand childDimension = ProductDimensions.createBrand("google"); ProductPartitionNode childNode = rootNode.addChild(childDimension); assertNull("Bid should be null by default", childNode.getBid()); childNode = childNode.setBid(1L); assertEquals("Bid does not reflect setBid", 1L, childNode.getBid().longValue()); assertTrue("Node should be a biddable unit", childNode.isBiddableUnit()); childNode = childNode.asExcludedUnit(); assertTrue("Node should be an excluded unit", childNode.isExcludedUnit()); assertFalse("Node should not be a biddable unit", childNode.isBiddableUnit()); assertNull("Excluded unit should have a null bid", childNode.getBid()); // Set back to biddable. childNode = childNode.asBiddableUnit(); assertTrue("Node should be a biddable unit", childNode.isBiddableUnit()); } @Test public void testNavigation() { rootNode = rootNode.asSubdivision(); ProductBrand brandGoogle = ProductDimensions.createBrand("google"); ProductBrand brandOther = ProductDimensions.createBrand(null); ProductCanonicalCondition conditionNew = ProductDimensions.createCanonicalCondition(ProductCanonicalConditionCondition.NEW); ProductCanonicalCondition conditionUsed = ProductDimensions.createCanonicalCondition(ProductCanonicalConditionCondition.USED); ProductCanonicalCondition conditionOther = ProductDimensions.createCanonicalCondition(null); // Build up the brand = Google node under the root. ProductPartitionNode brandGoogleNode = rootNode.addChild(brandGoogle).asSubdivision(); brandGoogleNode.addChild(conditionNew); brandGoogleNode.addChild(conditionUsed); brandGoogleNode.addChild(conditionOther); assertTrue("hasChild should return true for existing child dimension", brandGoogleNode.hasChild(conditionNew)); assertSame("parent->getChild->getParent should return parent", brandGoogleNode, brandGoogleNode.getChild(conditionNew).getParent()); assertTrue("hasChild should return true for existing child dimension", brandGoogleNode.hasChild(conditionUsed)); assertSame("parent->getChild->getParent should return parent", brandGoogleNode, brandGoogleNode.getChild(conditionUsed).getParent()); assertTrue("hasChild should return true for existing child dimension", brandGoogleNode.hasChild(conditionOther)); assertSame("parent->getChild->getParent should return parent", brandGoogleNode, brandGoogleNode.getChild(conditionOther).getParent()); // Build up the brand = null (other) node under the root. ProductPartitionNode brandOtherNode = rootNode.addChild(brandOther).asSubdivision(); brandOtherNode.addChild(conditionNew); assertTrue("hasChild should return true for existing child dimension", brandOtherNode.hasChild(conditionNew)); assertSame("parent->getChild->getParent should return parent", brandOtherNode, brandOtherNode.getChild(conditionNew).getParent()); assertFalse("hasChild should return false for nonexistent child dimension", brandOtherNode.hasChild(conditionUsed)); assertFalse("hasChild should return false for nonexistent child dimension", brandOtherNode.hasChild(conditionOther)); brandOtherNode.addChild(conditionOther); assertTrue("hasChild should return true for existing child dimension", brandOtherNode.hasChild(conditionOther)); assertSame("parent->getChild->getParent should return parent", brandOtherNode, brandOtherNode.getChild(conditionOther).getParent()); // Remove one of the children of brand = null. brandOtherNode.removeChild(conditionOther); assertFalse("hasChild should return false for a removed child dimension", brandOtherNode.hasChild(conditionOther)); // Remove the rest of the children of brand = null. brandOtherNode.removeAllChildren(); assertFalse("hasChild should return false for any removed child dimension", brandOtherNode.hasChild(conditionNew)); assertFalse("hasChild should return false for any removed child dimension", brandOtherNode.hasChild(conditionUsed)); } /** * Test to confirm that {@link ProductPartitionNode#toString(ProductDimension)} handles each * subclass of {@link ProductDimension}. This will help ensure that * {@link ProductPartitionNode#toString(ProductDimension)} is kept up to date with changes in * {@link ProductDimensions}. */ @Test public void testDimensionToString() throws Exception { // Use reflection to determine which subclasses of ProductDimension have a factory // method in ProductDimensions. Set<Class<? extends ProductDimension>> dimensionSubclasses = ProductDimensionsTest.getAllProductDimensionFactoryMethods().keySet(); // Create a sample instance for each subclass of ProductDimension. Map<Class<? extends ProductDimension>, ProductDimension> dimensionsByType = Maps.newHashMap(); dimensionsByType.put(ProductBrand.class, ProductDimensions.createBrand("testBrand")); dimensionsByType.put(ProductBiddingCategory.class, ProductDimensions.createBiddingCategory(ProductDimensionType.BIDDING_CATEGORY_L1, 123L)); dimensionsByType.put(ProductCanonicalCondition.class, ProductDimensions.createCanonicalCondition(ProductCanonicalConditionCondition.NEW)); dimensionsByType.put(ProductChannel.class, ProductDimensions.createChannel(ShoppingProductChannel.LOCAL)); dimensionsByType.put(ProductChannelExclusivity.class, ProductDimensions .createChannelExclusivity(ShoppingProductChannelExclusivity.SINGLE_CHANNEL)); dimensionsByType.put(ProductCustomAttribute.class, ProductDimensions.createCustomAttribute( ProductDimensionType.CUSTOM_ATTRIBUTE_2, "testAttribute")); dimensionsByType.put(ProductOfferId.class, ProductDimensions.createOfferId("testOfferId")); dimensionsByType.put(ProductType.class, ProductDimensions.createType(ProductDimensionType.PRODUCT_TYPE_L3, "testType")); // For each ProductDimension subclass, assert that we have a sample instance and that // ProductPartitionNode.toString(ProductDimension) produces a meaningful String for the // sample instance. for (Class<? extends ProductDimension> dimensionSubclass : dimensionSubclasses) { ProductDimension sampleDimension = dimensionsByType.get(dimensionSubclass); assertNotNull("No sample dimension found for ProductDimension subclass " + dimensionSubclass + ". Add a sample instance of this type to the dimensionsByType map in this test.", sampleDimension); String dimensionToString = ProductPartitionNode.toString(sampleDimension); assertThat("ProductPartitionNode.toString does not appear to recognize any attributes of " + dimensionSubclass, dimensionToString, Matchers.not(Matchers.containsString("[]"))); assertThat("ProductPartitionNode.toString failed to interpret the attributes of a " + dimensionSubclass, dimensionToString, Matchers.not(Matchers.containsString("UNKNOWN"))); } } }