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(); // Build up the brand = Google node under the root. ProductPartitionNode brandGoogleNode = rootNode.AddChild(brandGoogle).AsSubdivision(); brandGoogleNode.AddChild(conditionNew); brandGoogleNode.AddChild(conditionUsed); brandGoogleNode.AddChild(conditionOther); Assert.True(brandGoogleNode.HasChild(conditionNew), "HasChild should return true for existing child dimension."); Assert.AreSame(brandGoogleNode, brandGoogleNode.GetChild(conditionNew).Parent, "parent->GetChild->getParent should return parent."); Assert.True(brandGoogleNode.HasChild(conditionUsed), "HasChild should return true for existing child dimension."); Assert.AreSame(brandGoogleNode, brandGoogleNode.GetChild(conditionUsed).Parent, "parent->GetChild->getParent should return parent."); Assert.True(brandGoogleNode.HasChild(conditionOther), "HasChild should return true for existing child dimension."); Assert.AreSame(brandGoogleNode, brandGoogleNode.GetChild(conditionOther).Parent, "parent->GetChild->getParent should return parent."); // Build up the brand = null (other) node under the root. ProductPartitionNode brandOtherNode = rootNode.AddChild(brandOther).AsSubdivision(); brandOtherNode.AddChild(conditionNew); Assert.True(brandOtherNode.HasChild(conditionNew), "HasChild should return true for existing child dimension."); Assert.AreSame(brandOtherNode, brandOtherNode.GetChild(conditionNew).Parent, "parent->GetChild->getParent should return parent."); Assert.False(brandOtherNode.HasChild(conditionUsed), "HasChild should return false for nonexistent child dimension."); Assert.False(brandOtherNode.HasChild(conditionOther), "HasChild should return false for nonexistent child dimension."); brandOtherNode.AddChild(conditionOther); Assert.True(brandOtherNode.HasChild(conditionOther), "HasChild should return true for existing child dimension."); Assert.AreSame(brandOtherNode, brandOtherNode.GetChild(conditionOther).Parent, "parent->GetChild->getParent should return parent."); // Remove one of the children of brand = null. brandOtherNode.RemoveChild(conditionOther); Assert.False(brandOtherNode.HasChild(conditionOther), "HasChild should return false for a removed child dimension."); // Remove the rest of the children of brand = null. brandOtherNode.RemoveAllChildren(); Assert.False(brandOtherNode.HasChild(conditionNew), "HasChild should return false for any removed child dimension."); Assert.False(brandOtherNode.HasChild(conditionUsed), "HasChild should return false for any removed child dimension."); }
/// <summary> /// Takes a tree with: /// /// <pre> /// ROOT /// ... /// shoes /// new /// offerId=1 $1.00 /// offerId=2 $2.00 /// ... /// offerId=20 $20.00 /// other - excluded /// </pre> /// /// and changes it to: /// /// <pre> /// ROOT /// ... /// shoes /// new $1.25 /// other $0.50 /// </pre> /// </summary> private void CollapseNewShoes() { ProductPartitionNode shoesLevel1 = tree.Root.GetChild(ProductDimensions.CreateType( ProductDimensionType.PRODUCT_TYPE_L1, "shoes")); shoesLevel1.GetChild(ProductDimensions.CreateCanonicalCondition( ProductCanonicalConditionCondition.NEW)).AsBiddableUnit().CpcBid = 1250000L; shoesLevel1.GetChild(ProductDimensions.CreateCanonicalCondition()).AsBiddableUnit() .CpcBid = 500000L; Assert.DoesNotThrow(delegate() { tree = ExecuteTreeOperations(); }); }
public void TestGetChildThatDoesNotExistFails() { rootNode = rootNode.AsSubdivision(); Assert.Throws <ArgumentException>(delegate() { rootNode.GetChild(ProductDimensions.CreateBrand("google")); }); }
/// <summary> /// Subdivides the new shoes. /// </summary> /// Takes a tree with: /// <pre> /// ROOT /// ... /// shoes /// new some bid /// </pre> /// /// and changes it to: /// /// <pre> /// ROOT /// ... /// shoes /// new /// other offerId $1.00 /// offerId=2 $2.00 /// ... /// offerId=20 $20.00 /// </pre> private void SubdivideNewShoes() { ProductPartitionNode shoesLevel1 = tree.Root.GetChild(ProductDimensions.CreateType( ProductDimensionType.PRODUCT_TYPE_L1, "shoes")).AsSubdivision(); ProductPartitionNode newShoesLevel2 = shoesLevel1.GetChild( ProductDimensions.CreateCanonicalCondition(ProductCanonicalConditionCondition.NEW)) .AsSubdivision(); for (int i = 1; i <= 20; i++) { ProductOfferId offerId = ProductDimensions.CreateOfferId(); if (i > 1) { offerId.value = i.ToString(); } newShoesLevel2.AddChild(offerId).AsBiddableUnit().CpcBid = i * 1000000L; } Assert.DoesNotThrow(delegate() { tree = ExecuteTreeOperations(); }); }
public void TestMutateMultiNodeTree() { ProductPartitionTree tree = shoppingTestUtils.CreateTestTreeForTransformation(ADGROUP_ID); Assert.AreEqual(ADGROUP_ID, tree.AdGroupId, "ad group ID is incorrect"); // Change the bids on leaf nodes. ProductPartitionNode brandGoogleNode = tree.Root.GetChild(shoppingTestUtils.BRAND_GOOGLE); ProductPartitionNode offerANode = brandGoogleNode.GetChild(shoppingTestUtils.OFFER_A); // This should produce 1 SET operation. offerANode.CpcBid = offerANode.CpcBid * 10; // Offer B is changed from Exclude to Biddable. This should produce 1 // REMOVE operation + 1 ADD operation. ProductPartitionNode offerBNode = brandGoogleNode.GetChild(shoppingTestUtils.OFFER_B); offerBNode.AsBiddableUnit().CpcBid = 5000000L; // Other Brand node is changed from Exclude to Biddable. This should // produce 1 REMOVE operation + 1 ADD operation. ProductPartitionNode brandOtherNode = tree.Root.GetChild(shoppingTestUtils.BRAND_OTHER); brandOtherNode = brandOtherNode.AsBiddableUnit(); // Add an offer C node. This should produce 1 ADD operation. ProductPartitionNode offerCNode = brandGoogleNode.AddChild(shoppingTestUtils.OFFER_C); offerCNode.AsBiddableUnit().CpcBid = 1500000L; // Remove the brand Motorola node. This should produce 1 REMOVE operation. ProductPartitionNode brandMotorolaNode = tree.Root.GetChild( shoppingTestUtils.BRAND_MOTOROLA); tree.Root.RemoveChild(shoppingTestUtils.BRAND_MOTOROLA); // Get the mutate operations generated by the modifications made to the tree. AdGroupCriterionOperation[] mutateOperations = tree.GetMutateOperations(); Assert.AreEqual(7, mutateOperations.Length); List <AdGroupCriterionOperation> operations = null; // Since Offer A node only has modified attributes, there should only be // one SET operation. operations = shoppingTestUtils.GetOperationsForNode(offerANode, mutateOperations); Assert.That(operations.Count == 1); Assert.That(operations[0].@operator == Operator.SET); // Since Offer B node is being converted from Exclude to Biddable node, // there should be one REMOVE operation, and another ADD operation. operations = shoppingTestUtils.GetOperationsForNode(offerBNode, mutateOperations); Assert.That(operations.Count == 2); Assert.That(operations[0].@operator == Operator.REMOVE); Assert.That(operations[1].@operator == Operator.ADD); // Since Offer C node is being added, there should be one ADD operation. operations = shoppingTestUtils.GetOperationsForNode(offerCNode, mutateOperations); Assert.That(operations.Count == 1); Assert.That(operations[0].@operator == Operator.ADD); // Since Other Brand node is being converted from Exclude to Biddable node, // there should be one REMOVE operation, and another ADD operation. operations = shoppingTestUtils.GetOperationsForNode(brandOtherNode, mutateOperations); Assert.That(operations.Count == 2); Assert.That(operations[0].@operator == Operator.REMOVE); Assert.That(operations[1].@operator == Operator.ADD); // Since Offer B node is being removed, there should be one REMOVE // operation. operations = shoppingTestUtils.GetOperationsForNode(brandMotorolaNode, mutateOperations); Assert.That(operations.Count == 1); Assert.That(operations[0].@operator == Operator.REMOVE); }
/// <summary> /// Adds to the operations list all operations required to mutate /// <paramref name="originalNode"/> to the state* of /// <paramref name="newNode"/>. /// </summary> /// <param name="originalNode">The original node.</param> /// <param name="newNode">The new node.</param> /// <param name="ops">The operations list to add to.</param> /// <param name="idGenerator">The temporary ID generator for ADD operations.</param> /// <returns>The set of child product dimensions that require further /// processing.</returns> private void AddMutateOperations(ProductPartitionNode originalNode, ProductPartitionNode newNode, List<OperationPair> ops, TemporaryIdGenerator idGenerator) { NodeDifference nodeDifference = Diff(originalNode, newNode, dimensionComparator); bool isProcessChildren; switch (nodeDifference) { case NodeDifference.NEW_NODE: ops.AddRange(CreateAddOperations(newNode, idGenerator)); // No need to further process children. The ADD operations above will include operations // for all children of newNode. isProcessChildren = false; break; case NodeDifference.REMOVED_NODE: ops.Add(CreateRemoveOperation(originalNode)); // No need to further process children. The REMOVE operation above will perform a // cascading delete of all children of newNode. isProcessChildren = false; break; case NodeDifference.PARTITION_TYPE_CHANGE: case NodeDifference.EXCLUDED_UNIT_CHANGE: ops.Add(CreateRemoveOperation(originalNode)); ops.AddRange(CreateAddOperations(newNode, idGenerator)); // No need to further process children. The ADD operations above will include operations // for all children of newNode. isProcessChildren = false; break; case NodeDifference.BID_CHANGE: // Ensure that the new node has the proper ID (this may have been lost if the node // was removed and then re-added). newNode.ProductPartitionId = originalNode.ProductPartitionId; ops.Add(CreateSetBidOperation(newNode)); // Process the children of newNode. The SET operation above will only handle changes // made to newNode, not its children. isProcessChildren = true; break; case NodeDifference.NONE: // Ensure that the new node has the proper ID (this may have been lost if the node // was removed and then re-added). newNode.ProductPartitionId = originalNode.ProductPartitionId; // This node does not have changes, but its children may. isProcessChildren = true; break; default: throw new InvalidOperationException("Unrecognized difference: " + nodeDifference); } if (isProcessChildren) { // Try to match the children in new and original trees to identify the // matching dimensions. foreach (ProductPartitionNode newChild in newNode.Children) { if (originalNode.HasChild(newChild.Dimension)) { // this is probably an edit. AddMutateOperations(originalNode.GetChild(newChild.Dimension), newChild, ops, idGenerator); } else { // this is a new node. AddMutateOperations(null, newChild, ops, idGenerator); } } foreach (ProductPartitionNode originalChild in originalNode.Children) { if (newNode.HasChild(originalChild.Dimension)) { // this is probably an edit. We dealt with it before continue; } else { // this is a removed node. AddMutateOperations(originalChild, null, ops, idGenerator); } } } }
public void TestGetChildThatDoesNotExistFails() { rootNode = rootNode.AsSubdivision(); Assert.Throws<ArgumentException>(delegate() { rootNode.GetChild(ProductDimensions.CreateBrand("google")); }); }