package com.windowtester.test.eclipse.codegen; import java.io.BufferedInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import org.eclipse.core.runtime.Platform; import org.osgi.framework.Bundle; import com.windowtester.recorder.event.ISemanticEvent; import com.windowtester.runtime.WidgetSearchException; import com.windowtester.runtime.locator.IWidgetLocator; import com.windowtester.runtime.locator.XYLocator; import com.windowtester.runtime.swt.UITestCaseSWT; import com.windowtester.runtime.swt.locator.CTabItemLocator; import com.windowtester.runtime.util.TestMonitor; import com.windowtester.test.eclipse.EclipseUtil; import com.windowtester.test.util.PlatformEventWatcherAndCodegenerator; import com.windowtester.test.util.PlatformEventWatcherAndCodegenerator.API; /******************************************************************************* * Copyright (c) 2012 Google, Inc. * 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: * Google, Inc. - initial API and implementation *******************************************************************************/ public abstract class AbstractRecorderSmokeTest extends UITestCaseSWT { private static final boolean DISPLAY_EVENTS = true; private static final boolean DISPLAY_CODEGEN = true; static final String ECLIPSE_VERSION_SUFFIX = "_" + EclipseUtil.getMajor() + EclipseUtil.getMinor(); static final String EXPECTED_FILE_EXT = ".java"; static final String TEST_PACKAGE = "expected"; static final String OPTIONAL_TAG = "// ?"; //watcher is shared private static PlatformEventWatcherAndCodegenerator _watcher; private API _api = API.V2; //////////////////////////////////////////////////////////////////////////////////////// // // Lifecycle // //////////////////////////////////////////////////////////////////////////////////////// @Override protected void setUp() throws Exception { //we need to override this, to make the runtime THINK it is in recording mode //this matters because in non-recording mode exceptions cause shells to be closed, menus to be //dismissed etc... (that is, they are treated as ERRORS, instead of part of daily life //as they are treated in recording) TestMonitor.getInstance().endTestCase(); setUpCodegenDetails(); //start the recorder startWatching(); } @Override protected void tearDown() throws Exception { stopWatching(); } private void setUpCodegenDetails() { getWatcher().setTestName(getExpectedTestName()); getWatcher().setTestPackageName(TEST_PACKAGE); } private String getExpectedTestName() { String testName = getName(); if (testName.endsWith("Fails")) testName = testName.substring(0, testName.length() - 5); return testName; } private void startWatching() { getWatcher().watch(); } private void stopWatching() { getWatcher().stop(); } /** * Extend superclass behavior to compare generated code to expected code * after each test has been run. This should not be done in tearDown() method * because throwing an exception in tearDown() will suppress an exception * that has already occurred during test execution. * @see junit.framework.TestCase#runTest() */ protected void runTest() throws Throwable { super.runTest(); optionallyDisplayEvents(getWatcher()); String generatedTestCase = getWatcher().codegen(); optionallyDisplayGeneratedTest(generatedTestCase); assertSameAsFileContents(generatedTestCase, getExpectedUrl()); } //////////////////////////////////////////////////////////////////////////////////////// // // Accessors // //////////////////////////////////////////////////////////////////////////////////////// protected URL getExpectedUrl() { try { Bundle bundle = Platform.getBundle(getBundleName()); String entryPrefix = "src-test-resources/" + TEST_PACKAGE + "/" + getExpectedTestName(); URL url = bundle.getEntry(entryPrefix + ECLIPSE_VERSION_SUFFIX + EXPECTED_FILE_EXT); if (url == null) url = bundle.getEntry(entryPrefix + EXPECTED_FILE_EXT); if (url == null) throw new RuntimeException("Failed to find " + url); /* $codepro.preprocessor.if version < 3.2 $ return Platform.asLocalURL(url); $codepro.preprocessor.elseif version >= 3.2 $ */ return org.eclipse.core.runtime.FileLocator.toFileURL(url); /* $codepro.preprocessor.endif $ */ } catch (MalformedURLException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } protected String getBundleName() { return "com.windowtester_test"; } /** * Default is set to {@link API#V2}. Call {@link #setAPIVersion(API)} to override. * @return the current API version */ protected API getAPIVersion() { return _api; } /** * Default is set to {@link API#V2}; call this to override. */ protected void setAPIVersion(API version) { _api = version; } private PlatformEventWatcherAndCodegenerator getWatcher() { if (_watcher == null) _watcher = new PlatformEventWatcherAndCodegenerator(getAPIVersion()); return _watcher; } //////////////////////////////////////////////////////////////////////////////////////// // // Setup helpers // //////////////////////////////////////////////////////////////////////////////////////// /** * Recent versions of Eclipse do not close the welcome page when view * is opened. Make sure it gets closed. * @throws WidgetSearchException */ protected void closeWelcomePageIfNecessary() throws WidgetSearchException { IWidgetLocator[] welcomeTab = getUI().findAll(new CTabItemLocator("Welcome")); if (welcomeTab.length == 0) return; // TODO: compute x based on tab width to avoid font dependencies int x = 78; if (abbot.Platform.isOSX()) x = 95; else if (abbot.Platform.isLinux()) x = 100; getUI().click(new XYLocator(welcomeTab[0], x, 12)); } //////////////////////////////////////////////////////////////////////////////////////// // // Assertion helpers // //////////////////////////////////////////////////////////////////////////////////////// private void assertSameAsFileContents(String result, URL url) throws FileNotFoundException, IOException { println("------------------------"); String[] lines = result.split("\n"); LineNumberReader in = null; try { in = new LineNumberReader(new InputStreamReader(new BufferedInputStream(url.openStream()))); String str; String line; int i = 0; while ((str = in.readLine()) != null) { // RemoveEclipse version specific class name suffix (e.g. "_32") int offset = str.indexOf(ECLIPSE_VERSION_SUFFIX + " extends"); if (offset > -1) str = str.substring(0, offset) + str.substring(offset + ECLIPSE_VERSION_SUFFIX.length()); if (isOptional(str)) { str = trimOptional(str); //only advance index in case of match if (str.trim().equals(lines[i].trim())) ++i; } else { assertTrue("codegen missing line [" + i +"]"+ lines[i], i < lines.length); str = str.trim(); line = lines[i++].trim(); //notice we don't decrement i since lines start at 1 and not 0 assertEquals("expected: " + str + " but got: " + line + " at [" + i + "]", str, line); } } //notice we need to decrement i to back up one line since we've eagerly incremented above int lastLine = i-1; assertTrue("unexpected line [" + lastLine + "] in codegen:\n\t" + lines[lastLine], lastLine == lines.length-1); } finally { if (in != null) in.close(); } } static String trimOptional(String str) { str = str.trim(); int index = str.indexOf(OPTIONAL_TAG); if (index == -1) return str; return str.substring(0, index); } static boolean isOptional(String str) { return str.indexOf(OPTIONAL_TAG) > -1; } //////////////////////////////////////////////////////////////////////////////////////// // // Debugging // //////////////////////////////////////////////////////////////////////////////////////// private void optionallyDisplayGeneratedTest(String generatedTestCase) { if (DISPLAY_CODEGEN) { println("---- generated test: -----"); println(generatedTestCase); println("--------------------------"); } } private void optionallyDisplayEvents(PlatformEventWatcherAndCodegenerator watcher) { if (DISPLAY_EVENTS) { println("---- recorded events: ----"); displayEvents(watcher.getEvents()); println("--------------------------"); } } private void displayEvents(List<ISemanticEvent> events) { for (ISemanticEvent event : events) { println(event); } } private void println(Object obj) { System.out.println(obj); } }