Package rabbit.tracking.internal.trackers

Source Code of rabbit.tracking.internal.trackers.JavaTrackerTest

/*
* 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)];
  }

}
TOP

Related Classes of rabbit.tracking.internal.trackers.JavaTrackerTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.