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"));
       });
 }