// Copyright 2016 Twitter. 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.twitter.heron.packing; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.twitter.heron.common.basics.ByteAmount; import com.twitter.heron.common.basics.Pair; import com.twitter.heron.spi.packing.InstanceId; import com.twitter.heron.spi.packing.PackingPlan; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** * Utility methods for common test assertions related to packing */ public final class AssertPacking { private AssertPacking() { } /** * Verifies that the containerPlan has at least one bolt named boltName with ram equal to * expectedBoltRam and likewise for spouts. If notExpectedContainerRam is not null, verifies that * the container ram is not that. */ public static void assertContainers(Set<PackingPlan.ContainerPlan> containerPlans, String boltName, String spoutName, ByteAmount expectedBoltRam, ByteAmount expectedSpoutRam, ByteAmount notExpectedContainerRam) { boolean boltFound = false; boolean spoutFound = false; List<Integer> expectedInstanceIndecies = new ArrayList<>(); List<Integer> foundInstanceIndecies = new ArrayList<>(); int expectedInstanceIndex = 1; // Ram for bolt should be the value in component ram map for (PackingPlan.ContainerPlan containerPlan : containerPlans) { if (notExpectedContainerRam != null) { assertNotEquals( notExpectedContainerRam, containerPlan.getRequiredResource().getRam()); } for (PackingPlan.InstancePlan instancePlan : containerPlan.getInstances()) { expectedInstanceIndecies.add(expectedInstanceIndex++); foundInstanceIndecies.add(instancePlan.getTaskId()); if (instancePlan.getComponentName().equals(boltName)) { assertEquals("Unexpected bolt ram", expectedBoltRam, instancePlan.getResource().getRam()); boltFound = true; } if (instancePlan.getComponentName().equals(spoutName)) { assertEquals( "Unexpected spout ram", expectedSpoutRam, instancePlan.getResource().getRam()); spoutFound = true; } } } assertTrue("Bolt not found in any of the container plans: " + boltName, boltFound); assertTrue("Spout not found in any of the container plans: " + spoutName, spoutFound); Collections.sort(foundInstanceIndecies); assertEquals("Unexpected instance global id set found.", expectedInstanceIndecies, foundInstanceIndecies); } /** * Verifies that the containerPlan contains a specific number of instances for the given component. */ public static void assertNumInstances(Set<PackingPlan.ContainerPlan> containerPlans, String component, int numInstances) { int instancesFound = 0; for (PackingPlan.ContainerPlan containerPlan : containerPlans) { for (PackingPlan.InstancePlan instancePlan : containerPlan.getInstances()) { if (instancePlan.getComponentName().equals(component)) { instancesFound++; } } } assertEquals(numInstances, instancesFound); } /** * Verifies that the RAM allocated for every container in a packing plan is less than a given * maximum value. */ public static void assertContainerRam(Set<PackingPlan.ContainerPlan> containerPlans, ByteAmount maxRamforResources) { for (PackingPlan.ContainerPlan containerPlan : containerPlans) { assertTrue(String.format("Container with id %d requires more RAM (%s) than" + " the maximum RAM allowed (%s)", containerPlan.getId(), containerPlan.getRequiredResource().getRam(), maxRamforResources), containerPlan.getRequiredResource().getRam().lessOrEqual(maxRamforResources)); } } public static void assertPackingPlan(String expectedTopologyName, Pair<Integer, InstanceId>[] expectedComponentInstances, PackingPlan plan) { assertEquals(expectedTopologyName, plan.getId()); assertEquals("Unexpected number of instances: " + plan.getContainers(), expectedComponentInstances.length, plan.getInstanceCount().intValue()); // for every instance on a given container... Set<Integer> expectedContainerIds = new HashSet<>(); for (Pair<Integer, InstanceId> expectedComponentInstance : expectedComponentInstances) { // verify the expected container exists int containerId = expectedComponentInstance.first; InstanceId instanceId = expectedComponentInstance.second; assertTrue(String.format("Container with id %s not found", containerId), plan.getContainer(containerId).isPresent()); expectedContainerIds.add(containerId); // and that the instance exists on it boolean instanceFound = false; PackingPlan.ContainerPlan containerPlan = plan.getContainer(containerId).get(); for (PackingPlan.InstancePlan instancePlan : containerPlan.getInstances()) { if (instancePlan.getTaskId() == instanceId.getTaskId()) { instanceFound = true; assertEquals("Wrong componentName for task " + instancePlan.getTaskId(), instanceId.getComponentName(), instancePlan.getComponentName()); assertEquals("Wrong getComponentIndex for task " + instancePlan.getTaskId(), instanceId.getComponentIndex(), instancePlan.getComponentIndex()); break; } } assertTrue(String.format("Container (%s) did not include expected instance with taskId %d", containerPlan, instanceId.getTaskId()), instanceFound); } Map<Integer, PackingPlan.InstancePlan> taskIds = new HashMap<>(); Map<String, Set<PackingPlan.InstancePlan>> componentInstances = new HashMap<>(); for (PackingPlan.ContainerPlan containerPlan : plan.getContainers()) { for (PackingPlan.InstancePlan instancePlan : containerPlan.getInstances()) { // check for taskId collisions PackingPlan.InstancePlan collisionInstance = taskIds.get(instancePlan.getTaskId()); assertNull(String.format("Task id collision between instance %s and %s", instancePlan, collisionInstance), collisionInstance); taskIds.put(instancePlan.getTaskId(), instancePlan); // check for componentIndex collisions Set<PackingPlan.InstancePlan> instances = componentInstances.get(instancePlan.getComponentName()); if (instances != null) { for (PackingPlan.InstancePlan instance : instances) { assertTrue(String.format( "Component index collision between instance %s and %s", instance, instancePlan), instance.getComponentIndex() != instancePlan.getComponentIndex()); } } if (componentInstances.get(instancePlan.getComponentName()) == null) { componentInstances.put(instancePlan.getComponentName(), new HashSet<PackingPlan.InstancePlan>()); } componentInstances.get(instancePlan.getComponentName()).add(instancePlan); } } assertEquals(expectedContainerIds.size(), plan.getContainers().size()); } }