package gov.nasa.arc.mct.gui.actions; import gov.nasa.arc.mct.components.AbstractComponent; import gov.nasa.arc.mct.components.ObjectManager; import gov.nasa.arc.mct.gui.ContextAwareAction; import gov.nasa.arc.mct.gui.View; import gov.nasa.arc.mct.gui.actions.SaveAction.ObjectsSaveAction; import gov.nasa.arc.mct.gui.actions.SaveAction.ThisSaveAction; import gov.nasa.arc.mct.gui.housing.MCTContentArea; import gov.nasa.arc.mct.gui.housing.MCTHousing; import gov.nasa.arc.mct.gui.impl.ActionContextImpl; import gov.nasa.arc.mct.platform.spi.PersistenceProvider; import gov.nasa.arc.mct.platform.spi.Platform; import gov.nasa.arc.mct.platform.spi.PlatformAccess; import gov.nasa.arc.mct.platform.spi.WindowManager; import gov.nasa.arc.mct.policy.ExecutionResult; import gov.nasa.arc.mct.policy.PolicyContext; import gov.nasa.arc.mct.policy.PolicyInfo; import gov.nasa.arc.mct.services.component.PolicyManager; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.mockito.ArgumentMatcher; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class TestSaveAll { private Platform platform; private Platform mockPlatform; private Set<AbstractComponent> goodComponents = new HashSet<AbstractComponent>(); @BeforeClass public void setup() { platform = PlatformAccess.getPlatform(); mockPlatform = Mockito.mock(Platform.class); new PlatformAccess().setPlatform(mockPlatform); PolicyManager mockPolicyManager = Mockito.mock(PolicyManager.class); Mockito.when(mockPlatform.getPolicyManager()).thenReturn(mockPolicyManager); Answer<ExecutionResult> answer = new Answer<ExecutionResult> () { @Override public ExecutionResult answer(InvocationOnMock invocation) throws Throwable { PolicyContext context = (PolicyContext) invocation.getArguments()[1]; AbstractComponent comp = context.getProperty(PolicyContext.PropertyName.TARGET_COMPONENT.getName(), AbstractComponent.class); return new ExecutionResult(context, goodComponents.contains(comp), "" ); } }; Mockito.when(mockPolicyManager.execute(Mockito.eq(PolicyInfo.CategoryType.OBJECT_INSPECTION_POLICY_CATEGORY.getKey()), Mockito.<PolicyContext>any())).thenAnswer(answer); } @AfterClass public void teardown() { new PlatformAccess().setPlatform(platform); } @Test (dataProvider = "generateSavesComponentCases") public void testSaveAllSavesComponent(ContextAwareAction action, final boolean isDirty) { // Save All should save the parent component, as well as any children // returned by getModifiedObjects. Even when parent has not 'changed', // save all implies that changes to children are relevant to parent. // This verifies that parent is saved even when parent is not dirty. // Set up parent/child component such that SaveAll should be available final AbstractComponent child = generateComponent(true, true, true, null); final AbstractComponent comp = generateComponent(true, isDirty, true, child); // Elaborate mocking to simulate context menu activation MCTHousing mockHousing = Mockito.mock(MCTHousing.class); MCTContentArea mockContentArea = Mockito.mock(MCTContentArea.class); View mockView = Mockito.mock(View.class); ActionContextImpl mockContext = Mockito.mock(ActionContextImpl.class); Mockito.when(mockContext.getInspectorComponent()).thenReturn(comp); Mockito.when(mockContext.getTargetComponent()).thenReturn(comp); Mockito.when(mockContext.getTargetHousing()).thenReturn(mockHousing); Mockito.when(mockHousing.getContentArea()).thenReturn(mockContentArea); Mockito.when(mockContentArea.getHousedViewManifestation()).thenReturn(mockView); Mockito.when(mockView.getManifestedComponent()).thenReturn(comp); // Generate a new persistence provider each time PersistenceProvider persistence = Mockito.mock(PersistenceProvider.class); Mockito.when(mockPlatform.getPersistenceProvider()).thenReturn(persistence); // Simulate menu activation cycle; also, verify expectations (even // though redundant to test below) Assert.assertTrue(action.canHandle(mockContext)); Assert.assertTrue(action.isEnabled()); // Perform the action action.actionPerformed(Mockito.mock(ActionEvent.class)); // Finally, verify that the appropriate objects (both parent and child) // went to persistence Mockito.verify(persistence, Mockito.times(1)).persist( Mockito.argThat(new ArgumentMatcher<Collection<AbstractComponent>>() { @Override public boolean matches(Object argument) { return (argument instanceof Collection<?>) && ((Collection<?>) argument).contains(child) && (!isDirty ^ ((Collection<?>) argument).contains(comp)); } }) ); } @DataProvider public Object[][] generateSavesComponentCases() { Object[][] cases = new Object[4][]; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { cases[i*2+j] = new Object[] { i == 0 ? new ThisSaveAction() : new ObjectsSaveAction(), j == 0 // isDirty }; } } return cases; } @Test (dataProvider="generateTestCases") public void testSaveAllEnabled(ContextAwareAction action, AbstractComponent comp, boolean shouldHandle, boolean shouldBeEnabled) { // Elaborate mocking to simulate context menu activation MCTHousing mockHousing = Mockito.mock(MCTHousing.class); MCTContentArea mockContentArea = Mockito.mock(MCTContentArea.class); View mockView = Mockito.mock(View.class); ActionContextImpl mockContext = Mockito.mock(ActionContextImpl.class); Mockito.when(mockContext.getInspectorComponent()).thenReturn(comp); Mockito.when(mockContext.getTargetComponent()).thenReturn(comp); Mockito.when(mockContext.getTargetHousing()).thenReturn(mockHousing); Mockito.when(mockHousing.getContentArea()).thenReturn(mockContentArea); Mockito.when(mockContentArea.getHousedViewManifestation()).thenReturn(mockView); Mockito.when(mockView.getManifestedComponent()).thenReturn(comp); // Verify that enabled/disabled states match expectations Assert.assertEquals(action.canHandle(mockContext), shouldHandle); if (shouldHandle) { // Only verify isEnabled if case should be handled Assert.assertEquals(action.isEnabled(), shouldBeEnabled); } } @DataProvider public Object[][] generateTestCases() { Object[][] testCases = new Object[64][]; boolean truths[] = {false, true}; int i = 0; // Generate a variety of test cases based on expected response to policy for (boolean action : truths) { for (boolean validParent : truths) { for (boolean validChild : truths) { for (boolean isDirty : truths) { for (boolean isObjectManager : truths) { for (boolean hasChild : truths) { Object[] testCase = { action ? new ThisSaveAction() : new ObjectsSaveAction(), generateComponent(validParent, isDirty, isObjectManager, hasChild ? generateComponent(validChild, isDirty, isObjectManager, null) : null), true, (isDirty && validParent) || (isObjectManager && hasChild && validChild) }; testCases[i++] = testCase; } } } } } } return testCases; } private AbstractComponent generateComponent(boolean good, boolean dirty, boolean isObjectManager, AbstractComponent child) { String name = (good ? "valid " : "invalid ") + (dirty ? "dirty " : "nonDirty ") + (isObjectManager ? "objectManager " : "nonManager ") + (child != null ? "parent" : "leaf"); AbstractComponent mockComponent = Mockito.mock(AbstractComponent.class, name); Mockito.when(mockComponent.isDirty()).thenReturn(dirty); ObjectManager mockObjectManager = Mockito.mock(ObjectManager.class); if (isObjectManager) { Mockito.when(mockComponent.getCapability(ObjectManager.class)).thenReturn(mockObjectManager); } if (child != null) { Mockito.when(mockObjectManager.getAllModifiedObjects()).thenReturn(Collections.singleton(child)); } else { Mockito.when(mockObjectManager.getAllModifiedObjects()).thenReturn(Collections.<AbstractComponent>emptySet()); } if (good) { goodComponents.add(mockComponent); } return mockComponent; } @Test (dataProvider="generateWarningDialogCases") public void testWarningDialog(ContextAwareAction action, AbstractComponent comp, boolean confirm, boolean prompt, final Set<AbstractComponent> expect) { // Elaborate mocking to simulate context menu activation MCTHousing mockHousing = Mockito.mock(MCTHousing.class); MCTContentArea mockContentArea = Mockito.mock(MCTContentArea.class); View mockView = Mockito.mock(View.class); ActionContextImpl mockContext = Mockito.mock(ActionContextImpl.class); Mockito.when(mockContext.getInspectorComponent()).thenReturn(comp); Mockito.when(mockContext.getTargetComponent()).thenReturn(comp); Mockito.when(mockContext.getTargetHousing()).thenReturn(mockHousing); Mockito.when(mockHousing.getContentArea()).thenReturn(mockContentArea); Mockito.when(mockContentArea.getHousedViewManifestation()).thenReturn(mockView); Mockito.when(mockView.getManifestedComponent()).thenReturn(comp); // Generate a new persistence provider each time PersistenceProvider persistence = Mockito.mock(PersistenceProvider.class); Mockito.when(mockPlatform.getPersistenceProvider()).thenReturn(persistence); // Ensure dialog choice WindowManager windowing = Mockito.mock(WindowManager.class); Mockito.when(mockPlatform.getWindowManager()).thenReturn(windowing); Mockito.when(windowing.<Object>showInputDialog( Mockito.anyString(), Mockito.anyString(), Mockito.<Object[]>any(), Mockito.any(), Mockito.<Map<String,Object>>any())) .thenReturn(confirm ? "Save" : "Cancel"); // Verify that enabled/disabled states match expectations Assert.assertEquals(action.canHandle(mockContext), true); // Perform the action action.actionPerformed(null); // Verify that save was performed iff confirmed Mockito.verify(windowing, prompt ? Mockito.times(1) : Mockito.never()).showInputDialog( Mockito.anyString(), Mockito.anyString(), Mockito.<Object[]>any(), Mockito.any(), Mockito.<Map<String,Object>>any()); if (confirm || !prompt) { Mockito.verify(persistence).persist(Mockito.argThat(new ArgumentMatcher<Collection<AbstractComponent>>() { @Override public boolean matches(Object argument) { @SuppressWarnings("unchecked") Collection<AbstractComponent> arg = (Collection<AbstractComponent>) argument; boolean result = arg.size() == expect.size(); for (AbstractComponent ac : arg) { result &= expect.contains(ac); } return result; } })); } else { Mockito.verify(persistence, Mockito.never()).persist(Mockito.<Collection<AbstractComponent>>any()); } } @DataProvider public Object[][] generateWarningDialogCases() { List<Object[]> cases = new ArrayList<Object[]>(); for (boolean action : new boolean[] { false, true }) { for (boolean confirm : new boolean[] { false, true }) { for (boolean partial : new boolean[] { false, true } ) { AbstractComponent ac = generateWarningComponent(true, 3, partial ? 3 : 0); Set<AbstractComponent> expect = new HashSet<AbstractComponent>(); expect.add(ac); for (AbstractComponent c : ac.getCapability(ObjectManager.class).getAllModifiedObjects()) { if (goodComponents.contains(c)) { expect.add(c); } } cases.add(new Object[] { action ? new ThisSaveAction() : new ObjectsSaveAction(), ac, confirm, partial, // prompt expect }); } } } return cases.toArray(new Object[cases.size()][]); } private AbstractComponent generateWarningComponent(boolean good, int goodChildren, int badChildren) { String name = (good ? "valid " : "invalid "); AbstractComponent mockComponent = Mockito.mock(AbstractComponent.class, name); Set<AbstractComponent> children = new HashSet<AbstractComponent>(); for (boolean g : new boolean[] { false, true }) { for (int i = 0; i < (g ? goodChildren : badChildren); i++) { children.add(generateWarningComponent(g, 0, 0)); } } Mockito.when(mockComponent.isDirty()).thenReturn(true); ObjectManager mockObjectManager = Mockito.mock(ObjectManager.class); Mockito.when(mockComponent.getCapability(ObjectManager.class)).thenReturn(mockObjectManager); Mockito.when(mockObjectManager.getAllModifiedObjects()).thenReturn(children); if (good) { goodComponents.add(mockComponent); } return mockComponent; } }