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