Java tutorial
/* * Copyright 2010 The Rabbit Eclipse Plug-in Project * * 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 rabbit.tracking.internal.trackers; import rabbit.data.store.model.JavaEvent; import rabbit.tracking.internal.IdleDetector; import rabbit.tracking.internal.TrackingPlugin; import rabbit.tracking.internal.trackers.AbstractTracker; import rabbit.tracking.internal.trackers.JavaTracker; import static org.eclipse.jdt.internal.ui.actions.SelectionConverter.getElementAtOffset; import static org.eclipse.jdt.internal.ui.actions.SelectionConverter.getInput; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; import org.eclipse.jdt.ui.actions.OpenJavaPerspectiveAction; import org.eclipse.jdt.ui.wizards.JavaCapabilityConfigurationPage; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.TextSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.views.IViewDescriptor; import org.joda.time.Interval; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import static java.lang.String.format; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URI; import java.util.Observable; import java.util.Random; /** * @see JavaTracker */ @SuppressWarnings("restriction") public class JavaTrackerTest extends AbstractTrackerTest<JavaEvent> { private static IJavaProject project; private static IPackageFragment pkg; private static ICompilationUnit unit; @AfterClass public static void afterClass() { PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().closeAllEditors(false); } @BeforeClass public static void beforeClass() throws Exception { new OpenJavaPerspectiveAction().run(); // Creates the project: IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IProject proj = root.getProject(System.currentTimeMillis() + ""); JavaCapabilityConfigurationPage.createProject(proj, (URI) null, null); project = JavaCore.create(proj); JavaCapabilityConfigurationPage page = new JavaCapabilityConfigurationPage(); page.init(project, null, null, false); page.configureJavaProject(null); // Creates the package: IPackageFragmentRoot src = project.getAllPackageFragmentRoots()[0]; pkg = src.createPackageFragment("pkg", true, null); // Creates the class: String className = "Program"; StringBuilder builder = new StringBuilder(); builder.append(format("package %s;%n", pkg.getElementName())); builder.append(format("public class %s {%n", className)); builder.append(format("}%n")); String content = builder.toString(); unit = pkg.createCompilationUnit(className + ".java", content, true, null); unit.open(null); PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().closeAllEditors(false); } /** * Tests when the editor is no longer the active part: */ @Test public void testEditorDeactivated() throws Exception { final JavaEditor editor = closeAndOpenEditor(); // Set the editor to select the package declaration: int offset = getDocument(editor).get().indexOf(pkg.getElementName()); int len = pkg.getElementName().length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); // Run the tracker to capture the event: long preStart = System.currentTimeMillis(); tracker.setEnabled(true); // Start long postStart = System.currentTimeMillis(); Thread.sleep(20); long preEnd = System.currentTimeMillis(); // Sets another view to be active to cause the editor to lose focus: editor.getSite().getPage().showView(getRandomView().getId()); long postEnd = System.currentTimeMillis(); // One data should be in the collection (the selected package declaration): assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(getElementAtOffset(editor), event.getElement()); } /** * Tests that events are recorded properly with the different states of the * editor. */ @Test public void testEditorDeactivatedThenActivated() throws Exception { JavaEditor editor = closeAndOpenEditor(); // Set the editor to select the package declaration: int offset = getDocument(editor).get().indexOf(pkg.getElementName()); int len = pkg.getElementName().length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); // Now run the tracker to capture the event: long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(20); long preEnd = System.currentTimeMillis(); editor.getSite().getPage().showView(getRandomView().getId()); long postEnd = System.currentTimeMillis(); // One data should be in the collection (the selected package declaration): assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(getElementAtOffset(editor), event.getElement()); // Now we activate the editor again to see if the tracker will start to // track events again: tracker.flushData(); preStart = System.currentTimeMillis(); editor.getSite().getPage().activate(editor); postStart = System.currentTimeMillis(); Thread.sleep(20); preEnd = System.currentTimeMillis(); editor.getSite().getPage().showView(getRandomView().getId()); postEnd = System.currentTimeMillis(); // One data should be in the collection (the selected package declaration): assertEquals(1, tracker.getData().size()); event = tracker.getData().iterator().next(); start = event.getInterval().getStartMillis(); end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(getElementAtOffset(editor), event.getElement()); } /** * Test when the user changes from working on a Java element to another. */ @Test public void testElementChanged() throws Exception { JavaEditor editor = closeAndOpenEditor(); // Set the editor to select the package declaration: IDocument document = getDocument(editor); int offset = document.get().indexOf(pkg.getElementName()); int len = pkg.getElementName().length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); // Keeps the reference of the package declaration for testing latter: final IJavaElement element = getElementAtOffset(editor); // Run the tracker to capture the event: long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(20); // Change the element the user is working on: offset = document.get().indexOf(unit.getTypes()[0].getElementName()); int line = document.getLineOfOffset(offset); StyledText text = editor.getViewer().getTextWidget(); text.setCaretOffset(document.getLineOffset(line)); text.insert(" "); long preEnd = System.currentTimeMillis(); text.notifyListeners(SWT.KeyDown, new Event()); long postEnd = System.currentTimeMillis(); // One data should be in the collection (the selected package declaration): assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); assertEquals(element, event.getElement()); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); } /** * When the tracker is set to enable, but if there is no active workbench * window, no data will be collected. */ @Test public void testEnable_noActiveWorkbenchWindow() throws Exception { JavaEditor editor = closeAndOpenEditor(); // Set the editor to select the package declaration: int offset = getDocument(editor).get().indexOf(unit.getElementName()); int len = unit.getElementName().length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); // Open a new shell to cause the workbench window to lose focus: Shell shell = null; try { shell = new Shell(Display.getCurrent()); shell.open(); shell.forceActive(); tracker.setEnabled(true); Thread.sleep(30); tracker.setEnabled(false); assertEquals(0, tracker.getData().size()); } finally { if (shell != null) { shell.dispose(); } } } @Test public void testEnableThenDisable() throws Exception { JavaEditor editor = closeAndOpenEditor(); // Set the editor to select the package declaration: String className = unit.getTypes()[0].getElementName(); int offset = getDocument(editor).get().indexOf(className); int len = className.length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); // Run the tracker to capture the event: long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(30); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // One data should be in the collection (the selected package declaration): assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(getElementAtOffset(editor), event.getElement()); } /** * Test an event on an anonymous. This event should be filtered on save, so * that instead of showing a user spent x amount of time on the anonymous * class, we show that a user spent x amount of time on the anonymous's parent * type element (a method or a type that is not anonymous). */ @Test public void testFilter_anonymousClass() throws Exception { /* * Here we test that: a method containing an anonymous Runnable which also * contains another anonymous Runnable, and the most inner Runnable is * selected (to emulate that the user is working on that), then when filter * on save the data should indicate that the user has spent x amount of time * working on the method, not any of the Runnable's. */ JavaEditor editor = closeAndOpenEditor(); IDocument document = getDocument(editor); StringBuilder builder = new StringBuilder(); builder.append("void aMethod() {"); builder.append(" new Runnable() { "); builder.append(" public void run(){"); builder.append(" new Runnable() {"); builder.append(" public void run() {}"); builder.append(" };"); builder.append(" } "); builder.append(" };"); builder.append("}"); String content = document.get(); int offset = content.indexOf("{") + 1; int len = 0; document.replace(offset, len, builder.toString()); content = document.get(); offset = content.indexOf("Runnable", content.indexOf("Runnable") + 1); len = "Runnable".length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(30); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); IJavaElement element = getElementAtOffset(editor); // This two are to check we've set the selection right in this test: assertEquals(IJavaElement.TYPE, element.getElementType()); assertTrue(((IType) element).isAnonymous()); // getParent().getParent().getParent() will give us the method: assertEquals(IJavaElement.METHOD, event.getElement().getElementType()); assertEquals("aMethod", event.getElement().getElementName()); } /** * This test is for the same purpose as * {@link #testFilter_deletedElement_typeMembers()}, but with a little * difference, that is, if the whole Java file is deleted, all the data about * the Java elements in the file will be stored under the Java file, even * though it's deleted. * * @see #testFilter_deletedElement_typeMembers() */ @Test public void testFilter_deletedElement_mainType() throws Exception { // Create a new class: String newClassName = unit.getTypes()[0].getElementName() + "abc"; StringBuilder builder = new StringBuilder(); builder.append(format("package %s;%n", pkg.getElementName())); builder.append(format("public class %s {", newClassName)); builder.append(format("}%n")); ICompilationUnit myUnit = pkg.createCompilationUnit(newClassName + ".java", builder.toString(), true, null); JavaEditor editor = closeAndOpenEditor(myUnit); // Set the editor to select the package declaration: int offset = getDocument(editor).get().indexOf(pkg.getElementName()); int len = pkg.getElementName().length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); // Make sure we got the selection right: assertEquals(IJavaElement.PACKAGE_DECLARATION, getElementAtOffset(editor).getElementType()); // Run the tracker to capture the event: long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(35); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Delete the file: myUnit.getResource().delete(true, null); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); // One data should be in the collection (the parent of the previously // selected package declaration): assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); // Test the data is placed under the root element: assertEquals(myUnit, event.getElement()); } /** * Tests that if a Java element, a field for example, is deleted from the Java * source file, then when we save the tracker's data, the data about the * deleted field should be store as the parent's data. * <p> * For example: If we have a field called "fieldA" under the class "ClassA" * and the tracker has recorded that the user has spent 20 seconds working on * the field, but the field is then deleted from the class. So when the * tracker saves the data, instead of saving * "The user has spent 20 seconds working on fieldA" we store * "The user has spent 20 seconds working on classA". * </p> * <p> * Another important purpose of this feature is that: Then a user starts to * type a new java element, like a method, he/she knows what the name he/she * is going to type for the method, but we have no way of knowing that, so * lots of events may be recorded before he/she finishes typing the name. For * example, if the user want to type "hello" as the method name, there will be * events recorded about the java element "hel", or "hell", or "hello", we * only need one of them ("hello") but we also want to keep the time about the * invalid ones, so before we save the data, we check for non-existent java * elements, and instead of saving the data under those elements, we save the * data under the first existing parent of the elements, if all parents are * missing (e.g. deletes the file), we save it under the file parent, like * "File.java", even though the file has been deleted. * </p> * * @see #testFilter_deletedElement_mainType() */ @Test public void testFilter_deletedElement_typeMembers() throws Exception { final JavaEditor editor = closeAndOpenEditor(); final IDocument document = getDocument(editor); // Place a field in the body of the class, note that we don't want to add // errors to the class: String field = "private int aVeryUniqueFieldName = 0;"; int offset = document.get().lastIndexOf('}') - 1; int len = 0; document.replace(offset, len, field); // Set the editor to select the field: offset = document.get().indexOf(field); len = field.length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); // Run the tracker to capture the event: long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(25); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Keeps a reference to the field statement first, for testing latter: final IJavaElement element = getElementAtOffset(editor); // Now delete the field statement from the source file, note that there // is no need to save the document (and we should not save the document, // other tests may depend on it): offset = document.get().indexOf(field); len = field.length(); document.replace(offset, len, ""); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); // Gets the data, the data is remained in the tracker as long as we don't // enable it again (according to the contract of the tracker): // // One data should be in the collection // (the parent of the selected package declaration): assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); // Now check that the element is the parent of the package declaration // instead of the deleted package declaration itself: assertEquals(element.getParent(), event.getElement()); } /** * Test an event on an import statement. This event should be filtered on * save, so that instead of showing a user spent x amount of time on the * import statement , we show that a user spent x amount of time on the type * root (ITypeRoot) element, (a.k.a the Java file). */ @Test public void testFilter_existingElement_importStatement() throws Exception { final JavaEditor editor = closeAndOpenEditor(); final IDocument document = getDocument(editor); String importStatement = "import java.util.*;"; int offset = document.get().indexOf(";") + 1; // Position after package // declaration int len = 0; document.replace(offset, len, importStatement); offset = document.get().indexOf(importStatement); len = importStatement.length(); ITextSelection selection = new TextSelection(offset, len); editor.getSelectionProvider().setSelection(selection); long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(20); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(getInput(editor), event.getElement()); } /** * Test an event on a static initialiser. This event should not be filtered on * save. */ @Test public void testFilter_existingElement_initializer() throws Exception { JavaEditor editor = closeAndOpenEditor(); IDocument document = getDocument(editor); String staticName = "static"; String methodText = staticName + " {}"; int offset = document.get().indexOf("{") + 1; int len = 0; document.replace(offset, len, methodText); offset = document.get().indexOf(staticName); len = staticName.length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); IJavaElement element = getElementAtOffset(editor); // Check we got the selection right assertEquals(IJavaElement.INITIALIZER, element.getElementType()); long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(20); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(element, event.getElement()); } /** * Test an event on a method, that is a member of an anonymous class. This * event should be filtered so that we so the user has spent x amount of time * on the method's first non-anonymous parent. */ @Test public void testFilter_existingElement_methodParentIsAnonymous() throws Exception { JavaEditor editor = closeAndOpenEditor(); IDocument document = getDocument(editor); StringBuilder anonymous = new StringBuilder(); anonymous.append("void aMethod() {"); anonymous.append(" new Runnable() { "); anonymous.append(" public void run(){"); anonymous.append(" new Runnable() {"); anonymous.append(" public void run() {}"); anonymous.append(" };"); anonymous.append(" } "); anonymous.append(" };"); anonymous.append("}"); int offset = document.get().indexOf("{") + 1; int len = 0; document.replace(offset, len, anonymous.toString()); String content = document.get(); offset = content.indexOf("run", content.indexOf("run") + 1); len = "run".length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); IJavaElement element = getElementAtOffset(editor); // Make sure we got the selection right: assertEquals("run", element.getElementName()); long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(20); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals("aMethod", event.getElement().getElementName()); } /** * Test an event on a method, that is a member of a non-anonymous class. This * event should not be filtered on save. */ @Test public void testFilter_existingElement_methodParentNotAnonymous() throws Exception { JavaEditor editor = closeAndOpenEditor(); IDocument document = getDocument(editor); String methodName = "aMethodName"; String methodText = format("void %s() {}", methodName); int offset = document.get().indexOf("{") + 1; int length = 0; document.replace(offset, length, methodText); offset = document.get().indexOf(methodName); length = methodName.length(); ITextSelection selection = new TextSelection(offset, length); editor.getSelectionProvider().setSelection(selection); IJavaElement element = getElementAtOffset(editor); // Make sure we got the selection right: assertEquals(IJavaElement.METHOD, element.getElementType()); long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(30); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(element, event.getElement()); } /** * Test an event on an package declaration. This event should be filtered on * save, so that instead of showing a user spent x amount of time on the * package declaration , we show that a user spent x amount of time on the * main type element. */ @Test public void testFilter_existingElement_packageDeclaration() throws Exception { JavaEditor editor = closeAndOpenEditor(); IDocument document = getDocument(editor); int offset = document.get().indexOf(pkg.getElementName()); int len = pkg.getElementName().length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); IJavaElement element = getElementAtOffset(editor); // Make sure we got the selection right: assertEquals(IJavaElement.PACKAGE_DECLARATION, element.getElementType()); long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(30); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(element.getParent(), event.getElement()); } /** * Test an event on an anonymous type. This event should be filtered so that * we show the user has spent x amount of time on the type's first * non-anonymous parent. */ @Test public void testFilter_existingElement_typeAnonymous() throws Exception { JavaEditor editor = closeAndOpenEditor(); IDocument document = getDocument(editor); StringBuilder anonymous = new StringBuilder(); anonymous.append("void aMethod() {"); anonymous.append(" new Runnable() { "); anonymous.append(" public void run(){"); anonymous.append(" } "); anonymous.append(" };"); anonymous.append("}"); int offset = document.get().indexOf("{") + 1; int len = 0; document.replace(offset, len, anonymous.toString()); offset = document.get().indexOf("Runnable"); len = "Runnable".length(); ITextSelection selection = new TextSelection(offset, len); editor.getSelectionProvider().setSelection(selection); IJavaElement element = getElementAtOffset(editor); // Check that we got the selection right: assertEquals(IJavaElement.TYPE, element.getElementType()); long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(35); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals("aMethod", event.getElement().getElementName()); assertEquals(IJavaElement.METHOD, event.getElement().getElementType()); } /** * Test an event on an inner class. This event should be not filtered on save. */ @Test public void testFilter_existingElement_typeInner() throws Exception { JavaEditor editor = closeAndOpenEditor(); IDocument document = getDocument(editor); String innerClassName = "anInnerClassName"; String innerClassText = format("%nstatic class %s {}", innerClassName); int offset = document.get().indexOf("{") + 1; int len = 0; document.replace(offset, len, innerClassText); offset = document.get().indexOf(innerClassName); len = innerClassName.length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); IJavaElement element = getElementAtOffset(editor); assertEquals(IJavaElement.TYPE, element.getElementType()); long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(20); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); assertEquals(1, tracker.getData().size()); final JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(innerClassName, element.getElementName()); assertEquals(element, event.getElement()); } /** * Test an event on a normal Java type (not anonymous, not inner class). This * event should not be filtered on save. */ @Test public void testFilter_existingElement_typeNormal() throws Exception { JavaEditor editor = closeAndOpenEditor(); String className = unit.getTypes()[0].getElementName(); int offset = getDocument(editor).get().indexOf(className); int len = className.length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); IJavaElement element = getElementAtOffset(editor); assertEquals(IJavaElement.TYPE, element.getElementType()); long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(30); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); assertEquals(1, tracker.getData().size()); final JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(element, event.getElement()); } /** * Test an event on a field. This event should be filtered on save, so that * instead of showing a user spent x amount of time on the field, we show that * a user spent x amount of time on the field's parent type. */ @Test public void testFilter_exsitingElement_field() throws Exception { JavaEditor editor = closeAndOpenEditor(); IDocument document = getDocument(editor); String fieldName = "aFieldName"; String methodText = format("private int %s = 1;", fieldName); int offset = document.get().indexOf("{") + 1; int len = 0; document.replace(offset, len, methodText); offset = document.get().indexOf(fieldName); len = fieldName.length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); IJavaElement element = getElementAtOffset(editor); assertEquals(IJavaElement.FIELD, element.getElementType()); long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(20); long preEnd = System.currentTimeMillis(); tracker.setEnabled(false); long postEnd = System.currentTimeMillis(); // Ask the tracker to save the data, the data should be appropriately // filtered tracker.saveData(); assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); // The filtered event should be on the field's parent, not on the field // itself: assertEquals(element.getParent(), event.getElement()); } /** * Tests when the user becomes inactive. */ @Test public void testUserInactive() throws Exception { JavaEditor editor = closeAndOpenEditor(); // Set the editor to select the package declaration: int offset = getDocument(editor).get().indexOf(pkg.getElementName()); int len = pkg.getElementName().length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); // Run the tracker to capture the event: long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(30); long preEnd = System.currentTimeMillis(); callIdleDetectorToNotify(); long postEnd = System.currentTimeMillis(); // One data should be in the collection (the selected package declaration): assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(getElementAtOffset(editor), event.getElement()); } /** * Tests when the window lose focus. */ @Test public void testWindowDeactivated() throws Exception { JavaEditor editor = closeAndOpenEditor(); // Set the editor to select the package declaration: int offset = getDocument(editor).get().indexOf(pkg.getElementName()); int len = pkg.getElementName().length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); // Run the tracker to capture the event: long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(20); Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); try { // Minimize the shell to cause it to lose focus: long preEnd = System.currentTimeMillis(); shell.setMinimized(true); long postEnd = System.currentTimeMillis(); // One data should be in the collection (the selected package // declaration): assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(getElementAtOffset(editor), event.getElement()); } finally { shell.setMinimized(false); } } /** * Tests that events are recorded properly with the different states of the * window. */ @Test public void testWindowDeactivatedThenActivated() throws Exception { JavaEditor editor = closeAndOpenEditor(); // Set the editor to select the package declaration: int offset = getDocument(editor).get().indexOf(pkg.getElementName()); int len = pkg.getElementName().length(); editor.getSelectionProvider().setSelection(new TextSelection(offset, len)); Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); try { // Now run the tracker to capture the event: long preStart = System.currentTimeMillis(); tracker.setEnabled(true); long postStart = System.currentTimeMillis(); Thread.sleep(20); // Minimize the shell to cause it to lose focus: long preEnd = System.currentTimeMillis(); shell.setMinimized(true); long postEnd = System.currentTimeMillis(); // One data should be in the collection (the selected package // declaration): assertEquals(1, tracker.getData().size()); JavaEvent event = tracker.getData().iterator().next(); long start = event.getInterval().getStartMillis(); long end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(getElementAtOffset(editor), event.getElement()); // Restore the shell to see if tracker will start tracking again: tracker.flushData(); preStart = System.currentTimeMillis(); shell.setMinimized(false); postStart = System.currentTimeMillis(); Thread.sleep(25); // Minimise the shell to cause it to lose focus: preEnd = System.currentTimeMillis(); shell.setMinimized(true); postEnd = System.currentTimeMillis(); // One data should be in the collection (the selected package // declaration): assertEquals(1, tracker.getData().size()); event = tracker.getData().iterator().next(); start = event.getInterval().getStartMillis(); end = event.getInterval().getEndMillis(); checkTime(preStart, start, postStart, preEnd, end, postEnd); assertEquals(getElementAtOffset(editor), event.getElement()); } finally { shell.setMinimized(false); } } /** * Hacks the global idle detector to cause it to notify it's observers. */ protected void callIdleDetectorToNotify() throws Exception { Field isActive = IdleDetector.class.getDeclaredField("isActive"); isActive.setAccessible(true); Method setChanged = Observable.class.getDeclaredMethod("setChanged"); setChanged.setAccessible(true); Method notifyObservers = Observable.class.getDeclaredMethod("notifyObservers"); notifyObservers.setAccessible(true); IdleDetector detector = TrackingPlugin.getDefault().getIdleDetector(); detector.setRunning(true); isActive.set(detector, false); setChanged.invoke(detector); notifyObservers.invoke(detector); detector.setRunning(false); } /** * Closes all the editor in the workbench page, contents of editors are not * saved. Then opens the Java editor on {@link #unit}. * * @return The editor. */ protected JavaEditor closeAndOpenEditor() throws PartInitException { return closeAndOpenEditor(unit); } /** * Closes all the editor in the workbench page, contents of editors are not * saved. Then opens the Java editor on the file. * * @param unit The Java file to open. * @return The editor. */ protected JavaEditor closeAndOpenEditor(ICompilationUnit unit) throws PartInitException { IWorkbench workbench = PlatformUI.getWorkbench(); IWorkbenchPage page = workbench.getActiveWorkbenchWindow().getActivePage(); page.closeAllEditors(false); IFile file = (IFile) unit.getResource(); IEditorDescriptor editor = workbench.getEditorRegistry().getDefaultEditor(file.getName()); IEditorInput input = new FileEditorInput(file); return (JavaEditor) page.openEditor(input, editor.getId()); } @Override protected JavaEvent createEvent() { return new JavaEvent(new Interval(0, 1), JavaCore.create("=Enfo/src<enfo{EnfoPlugin.java")); } @Override protected AbstractTracker<JavaEvent> createTracker() { return new JavaTracker(); } /** * Gets the document from the editor. * * @param editor The editor. * @return The document. */ protected IDocument getDocument(JavaEditor editor) { IDocument doc = editor.getDocumentProvider().getDocument(editor.getEditorInput()); if (doc == null) { fail("Document is null"); } return doc; } private IViewDescriptor getRandomView() { IViewDescriptor[] v = PlatformUI.getWorkbench().getViewRegistry().getViews(); return v[new Random().nextInt(v.length)]; } }