001/*
002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     <a href="mailto:grenard@nuxeo.com">Guillaume</a>
016 */
017
018package org.nuxeo.functionaltests;
019
020import java.io.File;
021import java.lang.reflect.Field;
022import java.lang.reflect.Method;
023import java.util.List;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027import org.junit.internal.runners.statements.RunAfters;
028import org.junit.rules.TestWatchman;
029import org.junit.runners.model.FrameworkMethod;
030import org.junit.runners.model.Statement;
031import org.nuxeo.common.utils.URIUtils;
032import org.openqa.selenium.remote.RemoteWebDriver;
033
034/**
035 * Watchman to log info about the test and create snapshot on failure.
036 *
037 * @since 5.8
038 */
039public class LogTestWatchman extends TestWatchman {
040
041    protected static final Log log = LogFactory.getLog(AbstractTest.class);
042
043    protected String lastScreenshot;
044
045    protected String lastPageSource;
046
047    protected String filePrefix;
048
049    protected RemoteWebDriver driver;
050
051    protected String serverURL;
052
053    public LogTestWatchman(final RemoteWebDriver driver, final String serverURL) {
054        this.driver = driver;
055        this.serverURL = serverURL;
056    }
057
058    public LogTestWatchman() {
059    }
060
061    @Override
062    @SuppressWarnings("unchecked")
063    public Statement apply(final Statement base, final FrameworkMethod method, Object target) {
064        return new Statement() {
065            @Override
066            public void evaluate() throws Throwable {
067                starting(method);
068                try {
069                    if (base instanceof RunAfters) {
070                        // Hack JUnit: in order to take screenshot at the right
071                        // time we add through reflection an after
072                        // function that will be executed before all other
073                        // ones. See NXP-12742
074                        Field fAtersField = RunAfters.class.getDeclaredField("fAfters");
075                        fAtersField.setAccessible(true);
076
077                        List<FrameworkMethod> afters = (List<FrameworkMethod>) fAtersField.get(base);
078                        if (afters != null && !afters.isEmpty()) {
079                            try {
080                                // Improve this and instead of finding a
081                                // special function, we could register
082                                // functions specially annotated.
083                                FrameworkMethod first = afters.get(0);
084                                Method m = AbstractTest.class.getMethod("runBeforeAfters", (Class<?>[]) null);
085                                FrameworkMethod f = new FrameworkMethod(m);
086                                if (first != null && !first.equals(f)) {
087                                    afters.add(0, f);
088                                }
089                            } catch (NoSuchMethodException e) {
090                                // Do nothing
091                            }
092                        }
093                    }
094                    base.evaluate();
095                    succeeded(method);
096                } catch (Throwable t) {
097                    failed(t, method);
098                    throw t;
099                } finally {
100                    finished(method);
101                }
102            }
103        };
104    }
105
106    /**
107     * @deprecated since 5.9.2, use {@link ScreenshotTaker#dumpPageSource(org.openqa.selenium.WebDriver, String)}
108     *             instead.
109     */
110    @Deprecated
111    public File dumpPageSource(String filename) {
112        ScreenshotTaker taker = new ScreenshotTaker();
113        return taker.dumpPageSource(driver, filename);
114    }
115
116    @Override
117    public void failed(Throwable e, FrameworkMethod method) {
118        String className = getTestClassName(method);
119        String methodName = method.getName();
120        log.error(String.format("Test '%s#%s' failed", className, methodName), e);
121
122        if (lastScreenshot == null || lastPageSource == null) {
123            ScreenshotTaker taker = new ScreenshotTaker();
124
125            if (lastScreenshot == null) {
126                File temp = taker.takeScreenshot(driver, filePrefix);
127                lastScreenshot = temp != null ? temp.getAbsolutePath() : null;
128            }
129
130            if (lastPageSource == null) {
131                File temp = taker.dumpPageSource(driver, filePrefix);
132                lastPageSource = temp != null ? temp.getAbsolutePath() : null;
133            }
134
135        }
136        log.info(String.format("Created screenshot file named '%s'", lastScreenshot));
137        log.info(String.format("Created page source file named '%s'", lastPageSource));
138        super.failed(e, method);
139    }
140
141    @Override
142    public void finished(FrameworkMethod method) {
143        log.info(String.format("Finished test '%s#%s'", getTestClassName(method), method.getName()));
144        lastScreenshot = null;
145        lastPageSource = null;
146        super.finished(method);
147    }
148
149    public RemoteWebDriver getDriver() {
150        return driver;
151    }
152
153    public String getServerURL() {
154        return serverURL;
155    }
156
157    protected String getTestClassName(FrameworkMethod method) {
158        return method.getMethod().getDeclaringClass().getName();
159    }
160
161    protected void logOnServer(String message) {
162        if (driver != null) {
163            driver.get(String.format("%s/restAPI/systemLog?token=dolog&level=WARN&message=----- WebDriver: %s",
164                    serverURL, URIUtils.quoteURIPathComponent(message, true)));
165        } else {
166            log.warn(String.format("Cannot log on server message: %s", message));
167        }
168    }
169
170    public void runBeforeAfters() {
171        if (driver != null) {
172            ScreenshotTaker taker = new ScreenshotTaker();
173            lastScreenshot = taker.takeScreenshot(driver, filePrefix).getAbsolutePath();
174            lastPageSource = taker.dumpPageSource(driver, filePrefix).getAbsolutePath();
175        }
176    }
177
178    public void setDriver(RemoteWebDriver driver) {
179        this.driver = driver;
180    }
181
182    public void setServerURL(String serverURL) {
183        this.serverURL = serverURL;
184    }
185
186    @Override
187    public void starting(FrameworkMethod method) {
188        String message = String.format("Starting test '%s#%s'", getTestClassName(method), method.getName());
189        log.info(message);
190        String className = getTestClassName(method);
191        String methodName = method.getName();
192        filePrefix = String.format("screenshot-lastpage-%s-%s", className, methodName);
193        logOnServer(message);
194    }
195
196    @Override
197    public void succeeded(FrameworkMethod method) {
198        if (lastPageSource != null) {
199            new File(lastPageSource).delete();
200        }
201        if (lastScreenshot != null) {
202            new File(lastScreenshot).delete();
203        }
204    }
205
206    /**
207     * @deprecated since 5.9.2, use {@link ScreenshotTaker#takeScreenshot(org.openqa.selenium.WebDriver, String)}
208     *             instead.
209     */
210    @Deprecated
211    public File takeScreenshot(String filename) {
212        ScreenshotTaker taker = new ScreenshotTaker();
213        return taker.takeScreenshot(driver, filename);
214    }
215
216}