diff options
author | Patrick Tasse | 2017-07-26 18:24:28 +0000 |
---|---|---|
committer | Patrick Tasse | 2017-08-10 20:52:01 +0000 |
commit | 44af35e8c5e3e24f54157212019114b0d3abb275 (patch) | |
tree | 77b4f48540fd0cbf831bebd18b9a1b4e30c5b99a | |
parent | e0cb729a344254e2f027ab460d0e8515e3af4c1a (diff) | |
download | org.eclipse.tracecompass-44af35e8c5e3e24f54157212019114b0d3abb275.tar.gz org.eclipse.tracecompass-44af35e8c5e3e24f54157212019114b0d3abb275.tar.xz org.eclipse.tracecompass-44af35e8c5e3e24f54157212019114b0d3abb275.zip |
tmf: Detect and handle trace content change
When a resource change event indicates that the content of a trace has
changed, automatically delete the trace's supplementary files.
If the trace is currently opened, prompt the user before closing the
trace and deleting the supplementary files.
Change-Id: I58a7e154c1ff9a49b16b03f55fd9bd9144acac21
Signed-off-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-on: https://git.eclipse.org/r/102025
Reviewed-by: Hudson CI
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
6 files changed, 302 insertions, 22 deletions
diff --git a/releng/org.eclipse.tracecompass.integration.swtbot.tests/META-INF/MANIFEST.MF b/releng/org.eclipse.tracecompass.integration.swtbot.tests/META-INF/MANIFEST.MF index ac4acd1603..8b067f477d 100644 --- a/releng/org.eclipse.tracecompass.integration.swtbot.tests/META-INF/MANIFEST.MF +++ b/releng/org.eclipse.tracecompass.integration.swtbot.tests/META-INF/MANIFEST.MF @@ -11,6 +11,7 @@ Export-Package: org.eclipse.tracecompass.integration.swtbot.tests.markers, org.eclipse.tracecompass.integration.swtbot.tests.perspectives, org.eclipse.tracecompass.integration.swtbot.tests.projectexplorer Require-Bundle: org.apache.commons.compress, + org.apache.commons.io, org.apache.log4j, org.eclipse.core.resources, org.eclipse.core.runtime, @@ -28,6 +29,7 @@ Require-Bundle: org.apache.commons.compress, org.eclipse.tracecompass.tmf.ui, org.eclipse.tracecompass.tmf.ui.swtbot.tests, org.eclipse.ui, + org.eclipse.ui.views, org.eclipse.ui.views.log, org.junit, org.eclipse.tracecompass.tmf.ui.tests, diff --git a/releng/org.eclipse.tracecompass.integration.swtbot.tests/src/org/eclipse/tracecompass/integration/swtbot/tests/projectexplorer/ProjectExplorerRefreshTest.java b/releng/org.eclipse.tracecompass.integration.swtbot.tests/src/org/eclipse/tracecompass/integration/swtbot/tests/projectexplorer/ProjectExplorerRefreshTest.java new file mode 100644 index 0000000000..b235f5f81b --- /dev/null +++ b/releng/org.eclipse.tracecompass.integration.swtbot.tests/src/org/eclipse/tracecompass/integration/swtbot/tests/projectexplorer/ProjectExplorerRefreshTest.java @@ -0,0 +1,202 @@ +/****************************************************************************** + * Copyright (c) 2017 Ericsson + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Patrick Tasse - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.tracecompass.integration.swtbot.tests.projectexplorer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +import org.apache.commons.io.FileUtils; +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.Logger; +import org.apache.log4j.SimpleLayout; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Path; +import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; +import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner; +import org.eclipse.swtbot.swt.finder.keyboard.Keystrokes; +import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; +import org.eclipse.tracecompass.tmf.ui.project.model.TmfProjectRegistry; +import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.SWTBotUtils; +import org.eclipse.tracecompass.tmf.ui.tests.shared.WaitUtils; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +/** + * SWTBot test for testing Project Explorer Refresh action + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(SWTBotJunit4ClassRunner.class) +public class ProjectExplorerRefreshTest { + private static final String TRACE_PROJECT_NAME = "test"; + + /** The Log4j logger instance. */ + private static final Logger fLogger = Logger.getRootLogger(); + + private static final File TEST_TRACES_PATH = new File(new Path(TmfTraceManager.getTemporaryDirPath()).append("testtraces").toOSString()); + + private static final File TEST_FILE_KERNEL = new File(getPath("import/kernel-overlap-testing")); + private static final File TEST_FILE_KERNEL_CLASH = new File(getPath("import/z-clashes/kernel-overlap-testing")); + private static final File TEST_FILE_UST = new File(getPath("import/ust-overlap-testing")); + + private static SWTWorkbenchBot fBot; + + private static File fTracesFolder = null; + + private static String getPath(String relativePath) { + return new Path(TEST_TRACES_PATH.getAbsolutePath()).append(relativePath).toOSString(); + } + + /** + * Test Class setup + * + * @throws IOException + * on error + */ + @BeforeClass + public static void init() throws IOException { + TestDirectoryStructureUtil.generateTraceStructure(TEST_TRACES_PATH); + + SWTBotUtils.initialize(); + + /* Set up for SWTBot */ + SWTBotPreferences.TIMEOUT = 20000; /* 20 second timeout */ + SWTBotPreferences.KEYBOARD_LAYOUT = "EN_US"; + fLogger.removeAllAppenders(); + fLogger.addAppender(new ConsoleAppender(new SimpleLayout(), ConsoleAppender.SYSTEM_OUT)); + fBot = new SWTWorkbenchBot(); + + /* Close welcome view */ + SWTBotUtils.closeView("Welcome", fBot); + + /* Switch perspectives */ + SWTBotUtils.switchToTracingPerspective(); + + /* Finish waiting for eclipse to load */ + WaitUtils.waitForJobs(); + + SWTBotUtils.createProject(TRACE_PROJECT_NAME); + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(TRACE_PROJECT_NAME); + fTracesFolder = new File(Objects.requireNonNull(TmfProjectRegistry.getProject(project, true).getTracesFolder()).getResource().getLocation().toOSString()); + } + + /** + * Test class tear down method. + */ + @AfterClass + public static void tearDown() { + SWTBotUtils.deleteProject(TRACE_PROJECT_NAME, fBot); + fLogger.removeAllAppenders(); + } + + /** + * Test tear down method. + */ + @After + public void afterTest() { + SWTBotUtils.closeSecondaryShells(fBot); + } + + /** + * Test Refresh after adding a trace on the file system. + * + * @throws IOException if an exception occurs + */ + @Test + public void test16_01RefreshTraceAdded() throws IOException { + FileUtils.copyDirectory(TEST_FILE_KERNEL, FileUtils.getFile(fTracesFolder, TEST_FILE_KERNEL.getName())); + FileUtils.copyDirectory(TEST_FILE_UST, FileUtils.getFile(fTracesFolder, TEST_FILE_UST.getName())); + SWTBotTreeItem tracesFolder = SWTBotUtils.selectTracesFolder(fBot, TRACE_PROJECT_NAME); + assertEquals(0, tracesFolder.getItems().length); + tracesFolder.contextMenu().menu("Refresh").click(); + SWTBotTreeItem kernelTrace = SWTBotUtils.getTraceProjectItem(fBot, tracesFolder, TEST_FILE_KERNEL.getName()); + kernelTrace.contextMenu().menu("Select Trace Type...", "Common Trace Format", "Linux Kernel Trace").click(); + SWTBotTreeItem ustTrace = SWTBotUtils.getTraceProjectItem(fBot, tracesFolder, TEST_FILE_UST.getName()); + ustTrace.contextMenu().menu("Select Trace Type...", "Common Trace Format", "LTTng UST Trace").click(); + } + + /** + * Test Refresh after modifying trace content while trace is opened. + * + * @throws IOException if an exception occurs + */ + @Test + public void test16_02RefreshOpenedTraceContentModified() throws IOException { + SWTBotTreeItem tracesFolder = SWTBotUtils.selectTracesFolder(fBot, TRACE_PROJECT_NAME); + SWTBotTreeItem kernelTrace = SWTBotUtils.getTraceProjectItem(fBot, tracesFolder, TEST_FILE_KERNEL.getName()); + kernelTrace.contextMenu().menu("Open").click(); + SWTBotUtils.activateEditor(fBot, TEST_FILE_KERNEL.getName()); + FileUtils.copyDirectory(TEST_FILE_KERNEL_CLASH, FileUtils.getFile(fTracesFolder, TEST_FILE_KERNEL.getName()), false); + assertTrue(kernelTrace.contextMenu().menuItems().contains("Delete Supplementary Files...")); + tracesFolder.contextMenu().menu("Refresh").click(); + SWTBotShell shell = fBot.shell("Trace Changed"); + shell.bot().button("No").click(); + assertTrue(kernelTrace.contextMenu().menuItems().contains("Delete Supplementary Files...")); + SWTBotUtils.activateEditor(fBot, TEST_FILE_KERNEL.getName()); + FileUtils.copyDirectory(TEST_FILE_KERNEL, FileUtils.getFile(fTracesFolder, TEST_FILE_KERNEL.getName()), false); + assertTrue(kernelTrace.contextMenu().menuItems().contains("Delete Supplementary Files...")); + tracesFolder.contextMenu().menu("Refresh").click(); + shell = fBot.shell("Trace Changed"); + shell.bot().button("Yes").click(); + assertFalse(kernelTrace.contextMenu().menuItems().contains("Delete Supplementary Files...")); + assertEquals(0, fBot.editors().size()); + } + + /** + * Test Refresh after modifying trace content while trace is closed. + * + * @throws IOException if an exception occurs + */ + @Test + public void test16_03RefreshClosedTraceContentModified() throws IOException { + SWTBotTreeItem tracesFolder = SWTBotUtils.selectTracesFolder(fBot, TRACE_PROJECT_NAME); + SWTBotTreeItem kernelTrace = SWTBotUtils.getTraceProjectItem(fBot, tracesFolder, TEST_FILE_KERNEL.getName()); + kernelTrace.contextMenu().menu("Open").click(); + SWTBotUtils.activateEditor(fBot, TEST_FILE_KERNEL.getName()).close(); + FileUtils.touch(FileUtils.getFile(fTracesFolder, TEST_FILE_KERNEL.getName(), "channel1")); + assertTrue(kernelTrace.contextMenu().menuItems().contains("Delete Supplementary Files...")); + tracesFolder.select().pressShortcut(Keystrokes.F5); + assertFalse(kernelTrace.contextMenu().menuItems().contains("Delete Supplementary Files...")); + } + + /** + * Test Refresh after deleting a trace on the file system. + * + * @throws IOException if an exception occurs + */ + @Test + public void test16_04RefreshTraceDeleted() throws IOException { + SWTBotTreeItem tracesFolder = SWTBotUtils.selectTracesFolder(fBot, TRACE_PROJECT_NAME); + assertEquals(2, tracesFolder.getItems().length); + SWTBotUtils.getTraceProjectItem(fBot, tracesFolder, TEST_FILE_KERNEL.getName()); + SWTBotUtils.getTraceProjectItem(fBot, tracesFolder, TEST_FILE_UST.getName()); + FileUtils.deleteDirectory(FileUtils.getFile(fTracesFolder, TEST_FILE_UST.getName())); + fBot.menu().menu("File", "Refresh").click(); + assertEquals(1, tracesFolder.getItems().length); + SWTBotUtils.getTraceProjectItem(fBot, tracesFolder, TEST_FILE_KERNEL.getName()); + } + +} diff --git a/releng/org.eclipse.tracecompass.integration.swtbot.tests/src/org/eclipse/tracecompass/integration/swtbot/tests/projectexplorer/TestDirectoryStructureUtil.java b/releng/org.eclipse.tracecompass.integration.swtbot.tests/src/org/eclipse/tracecompass/integration/swtbot/tests/projectexplorer/TestDirectoryStructureUtil.java index 7323c5ea9f..8ad13eb8b9 100644 --- a/releng/org.eclipse.tracecompass.integration.swtbot.tests/src/org/eclipse/tracecompass/integration/swtbot/tests/projectexplorer/TestDirectoryStructureUtil.java +++ b/releng/org.eclipse.tracecompass.integration.swtbot.tests/src/org/eclipse/tracecompass/integration/swtbot/tests/projectexplorer/TestDirectoryStructureUtil.java @@ -120,32 +120,32 @@ public class TestDirectoryStructureUtil { * │ ├── ExampleCustomTxt.log * │ ├── ExampleCustomXml.xml * │ ├── kernel-overlap-testing - * │ │ ├── stream + * │ │ ├── channel0 * │ │ └── metadata * │ ├── simple_server-thread1 - * │ │ ├── stream + * │ │ ├── channel0 * │ │ └── metadata * │ ├── simple_server-thread2 - * │ │ ├── stream + * │ │ ├── channel0 * │ │ └── metadata * │ └── ust-overlap-testing - * │ ├── stream + * │ ├── channel0 * │ └── metadata * ├── empty * ├── ExampleCustomTxt.log * ├── ExampleCustomXml.xml * ├── kernel-overlap-testing - * │ ├── stream + * │ ├── channel0 * │ └── metadata * ├── simple_server-thread1 - * │ ├── metadata - * │ └── stream + * │ ├── channel0 + * │ └── metadata * ├── simple_server-thread2 - * │ ├── metadata - * │ └── stream + * │ ├── channel0 + * │ └── metadata * ├── unrecognized.log * └── ust-overlap-testing - * ├── stream + * ├── channel0 * └── metadata * </pre> * diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/Messages.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/Messages.java index 868b2d5ace..c2f2c60a32 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/Messages.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/Messages.java @@ -155,6 +155,18 @@ public class Messages extends NLS { * @since 2.3 */ public static String TmfProject_TracesFolderNotExists; + + /** + * Trace Changed dialog title + * @since 3.1 + */ + public static String TmfProjectRegistry_TraceChangedDialogTitle; + /** + * Trace Changed dialog message + * @since 3.1 + */ + public static String TmfProjectRegistry_TraceChangedDialogMessage; + static { // initialize resource bundle NLS.initializeMessages(BUNDLE_NAME, Messages.class); diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/TmfProjectRegistry.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/TmfProjectRegistry.java index ef52a6152b..a527ec030f 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/TmfProjectRegistry.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/TmfProjectRegistry.java @@ -16,10 +16,13 @@ package org.eclipse.tracecompass.tmf.ui.project.model; import java.lang.reflect.InvocationTargetException; import java.net.URI; +import java.util.ArrayDeque; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.Set; import org.eclipse.core.resources.IFolder; @@ -34,13 +37,18 @@ import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; import org.eclipse.tracecompass.internal.tmf.ui.Activator; import org.eclipse.tracecompass.tmf.core.TmfCommonConstants; import org.eclipse.tracecompass.tmf.core.TmfProjectNature; import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager; import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.WorkspaceModifyOperation; @@ -58,6 +66,8 @@ public class TmfProjectRegistry implements IResourceChangeListener { // The map of project resource to project model elements private static Map<IProject, TmfProjectElement> registry = new HashMap<>(); + private static Queue<TmfTraceElement> promptQueue = new ArrayDeque<>(); + private TmfProjectRegistry() { ResourcesPlugin.getWorkspace().addResourceChangeListener(this); TmfSignalManager.register(this); @@ -206,18 +216,12 @@ public class TmfProjectRegistry implements IResourceChangeListener { project.isOpen() && project.hasNature(TmfProjectNature.ID)) { Set<IResource> resourcesToRefresh = new HashSet<>(); event.getDelta().accept(visited -> { - if (resourcesToRefresh.contains(visited.getResource().getParent())) { - return false; - } else if ((visited.getFlags() & IResourceDelta.CONTENT) != 0) { - // visited resource content has changed + if ((visited.getFlags() & IResourceDelta.CONTENT) != 0) { + // visited resource content has changed, refresh it resourcesToRefresh.add(visited.getResource()); - return false; } else if (visited.getKind() != IResourceDelta.CHANGED) { - // visited resource is added or removed - IResource parent = visited.getResource().getParent(); - resourcesToRefresh.add(parent); - resourcesToRefresh.removeIf(resource -> resource.getParent().equals(parent)); - return false; + // visited resource is added or removed, refresh its parent + resourcesToRefresh.add(visited.getResource().getParent()); } return true; }); @@ -228,7 +232,27 @@ public class TmfProjectRegistry implements IResourceChangeListener { elementsToRefresh.add(element); } } - elementsToRefresh.forEach(element -> element.refresh()); + Set<TmfTraceElement> changedTraces = new HashSet<>(); + Iterator<ITmfProjectModelElement> iterator = elementsToRefresh.iterator(); + while (iterator.hasNext()) { + ITmfProjectModelElement element = iterator.next(); + if (element instanceof TmfTraceElement) { + changedTraces.add(((TmfTraceElement) element).getElementUnderTraceFolder()); + } + for (ITmfProjectModelElement parent : elementsToRefresh) { + // remove element if any of its parents is included + if (parent.getPath().isPrefixOf(element.getPath()) && !parent.equals(element)) { + iterator.remove(); + break; + } + } + } + for (TmfTraceElement trace : changedTraces) { + handleTraceContentChanged(trace); + } + for (ITmfProjectModelElement element : elementsToRefresh) { + element.refresh(); + } TmfProjectElement projectElement = registry.get(project); if (projectElement != null) { // refresh only the viewer for the affected project @@ -313,4 +337,41 @@ public class TmfProjectRegistry implements IResourceChangeListener { } return element; } + + private static void handleTraceContentChanged(TmfTraceElement traceElement) { + Display.getDefault().asyncExec(() -> { + boolean opened = false; + for (ITmfTrace openedTrace : TmfTraceManager.getInstance().getOpenedTraces()) { + for (ITmfTrace trace : TmfTraceManager.getTraceSet(openedTrace)) { + if (traceElement.getResource().equals(trace.getResource())) { + opened = true; + break; + } + } + } + if (!opened) { + traceElement.deleteSupplementaryResources(); + return; + } + if (!promptQueue.isEmpty()) { + /* already prompting the user */ + if (!promptQueue.contains(traceElement)) { + promptQueue.add(traceElement); + } + return; + } + promptQueue.add(traceElement); + TmfTraceElement prompting = traceElement; + while (prompting != null) { + Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); + if (MessageDialog.openQuestion(shell, Messages.TmfProjectRegistry_TraceChangedDialogTitle, + NLS.bind(Messages.TmfProjectRegistry_TraceChangedDialogMessage, prompting.getElementPath()))) { + traceElement.closeEditors(); + traceElement.deleteSupplementaryResources(); + } + promptQueue.remove(); + prompting = promptQueue.peek(); + } + }); + } } diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/messages.properties b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/messages.properties index c252da1039..6c9c762841 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/messages.properties +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/project/model/messages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2013, 2016 Ericsson +# Copyright (c) 2013, 2017 Ericsson # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -55,3 +55,6 @@ TmfOpenTraceHelper_TraceNotFound = Trace {0} not found. TmfProject_ExperimentFolderNotExists = Root experiment folder doesn't exist in project. TmfProject_TracesFolderNotExists = Root traces folder doesn't exist in project + +TmfProjectRegistry_TraceChangedDialogTitle=Trace Changed +TmfProjectRegistry_TraceChangedDialogMessage=The trace ''{0}'' has been changed on the file system. Do you want to close the trace and delete its supplementary files?
\ No newline at end of file |