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 static org.nuxeo.functionaltests.AbstractTest.NUXEO_URL;
023import static org.nuxeo.functionaltests.Constants.ADMINISTRATOR;
024
025import java.io.File;
026import java.lang.reflect.Field;
027import java.lang.reflect.Method;
028import java.util.List;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.junit.internal.runners.statements.RunAfters;
033import org.junit.rules.TestWatchman;
034import org.junit.runners.model.FrameworkMethod;
035import org.junit.runners.model.Statement;
036import org.nuxeo.client.api.Client;
037import org.nuxeo.client.api.NuxeoClient;
038import org.nuxeo.common.utils.URIUtils;
039import org.openqa.selenium.remote.RemoteWebDriver;
040
041import okhttp3.Response;
042
043/**
044 * Watchman to log info about the test and create snapshot on failure.
045 *
046 * @since 5.8
047 */
048public class LogTestWatchman extends TestWatchman {
049
050    protected static final Log log = LogFactory.getLog(AbstractTest.class);
051
052    protected String lastScreenshot;
053
054    protected String lastPageSource;
055
056    protected String filePrefix;
057
058    protected RemoteWebDriver driver;
059
060    protected String serverURL;
061
062    public LogTestWatchman(final RemoteWebDriver driver, final String serverURL) {
063        this.driver = driver;
064        this.serverURL = serverURL;
065    }
066
067    public LogTestWatchman() {
068    }
069
070    @Override
071    @SuppressWarnings("unchecked")
072    public Statement apply(final Statement base, final FrameworkMethod method, Object target) {
073        return new Statement() {
074            @Override
075            public void evaluate() throws Throwable {
076                starting(method);
077                try {
078                    if (base instanceof RunAfters) {
079                        // Hack JUnit: in order to take screenshot at the right
080                        // time we add through reflection an after
081                        // function that will be executed before all other
082                        // ones. See NXP-12742
083                        Field fAtersField = RunAfters.class.getDeclaredField("fAfters");
084                        fAtersField.setAccessible(true);
085
086                        List<FrameworkMethod> afters = (List<FrameworkMethod>) fAtersField.get(base);
087                        if (afters != null && !afters.isEmpty()) {
088                            try {
089                                // Improve this and instead of finding a
090                                // special function, we could register
091                                // functions specially annotated.
092                                FrameworkMethod first = afters.get(0);
093                                Method m = AbstractTest.class.getMethod("runBeforeAfters", (Class<?>[]) null);
094                                FrameworkMethod f = new FrameworkMethod(m);
095                                if (first != null && !first.equals(f)) {
096                                    afters.add(0, f);
097                                }
098                            } catch (NoSuchMethodException e) {
099                                // Do nothing
100                            }
101                        }
102                    }
103                    base.evaluate();
104                    succeeded(method);
105                } catch (Throwable t) {
106                    failed(t, method);
107                    throw t;
108                } finally {
109                    finished(method);
110                }
111            }
112        };
113    }
114
115    /**
116     * @deprecated since 5.9.2, use {@link ScreenshotTaker#dumpPageSource(org.openqa.selenium.WebDriver, String)}
117     *             instead.
118     */
119    @Deprecated
120    public File dumpPageSource(String filename) {
121        ScreenshotTaker taker = new ScreenshotTaker();
122        return taker.dumpPageSource(driver, filename);
123    }
124
125    @Override
126    public void failed(Throwable e, FrameworkMethod method) {
127        String className = getTestClassName(method);
128        String methodName = method.getName();
129        log.error(String.format("Test '%s#%s' failed", className, methodName), e);
130
131        if (lastScreenshot == null || lastPageSource == null) {
132            ScreenshotTaker taker = new ScreenshotTaker();
133
134            if (lastScreenshot == null) {
135                File temp = taker.takeScreenshot(driver, filePrefix);
136                lastScreenshot = temp != null ? temp.getAbsolutePath() : null;
137            }
138
139            if (lastPageSource == null) {
140                File temp = taker.dumpPageSource(driver, filePrefix);
141                lastPageSource = temp != null ? temp.getAbsolutePath() : null;
142            }
143
144        }
145        log.info(String.format("Created screenshot file named '%s'", lastScreenshot));
146        log.info(String.format("Created page source file named '%s'", lastPageSource));
147        super.failed(e, method);
148    }
149
150    @Override
151    public void finished(FrameworkMethod method) {
152        log.info(String.format("Finished test '%s#%s'", getTestClassName(method), method.getName()));
153        lastScreenshot = null;
154        lastPageSource = null;
155        super.finished(method);
156    }
157
158    public RemoteWebDriver getDriver() {
159        return driver;
160    }
161
162    public String getServerURL() {
163        return serverURL;
164    }
165
166    protected String getTestClassName(FrameworkMethod method) {
167        return method.getMethod().getDeclaringClass().getName();
168    }
169
170    protected void logOnServer(String message) {
171        if (driver != null) {
172            Client client = new NuxeoClient(NUXEO_URL, ADMINISTRATOR, ADMINISTRATOR);
173            Response response = client.get(NUXEO_URL + "/restAPI/systemLog");
174            if (response.isSuccessful()) {
175                driver.get(String.format("%s/restAPI/systemLog?token=dolog&level=WARN&message=----- WebDriver: %s",
176                        serverURL, URIUtils.quoteURIPathComponent(message, true)));
177                return;
178            }
179        }
180        log.warn(String.format("Cannot log on server message: %s", message));
181    }
182
183    public void runBeforeAfters() {
184        if (driver != null) {
185            ScreenshotTaker taker = new ScreenshotTaker();
186            lastScreenshot = taker.takeScreenshot(driver, filePrefix).getAbsolutePath();
187            lastPageSource = taker.dumpPageSource(driver, filePrefix).getAbsolutePath();
188        }
189    }
190
191    public void setDriver(RemoteWebDriver driver) {
192        this.driver = driver;
193    }
194
195    public void setServerURL(String serverURL) {
196        this.serverURL = serverURL;
197    }
198
199    @Override
200    public void starting(FrameworkMethod method) {
201        String message = String.format("Starting test '%s#%s'", getTestClassName(method), method.getName());
202        log.info(message);
203        String className = getTestClassName(method);
204        String methodName = method.getName();
205        filePrefix = String.format("screenshot-lastpage-%s-%s", className, methodName);
206        logOnServer(message);
207    }
208
209    @Override
210    public void succeeded(FrameworkMethod method) {
211        if (lastPageSource != null) {
212            new File(lastPageSource).delete();
213        }
214        if (lastScreenshot != null) {
215            new File(lastScreenshot).delete();
216        }
217    }
218
219    /**
220     * @deprecated since 5.9.2, use {@link ScreenshotTaker#takeScreenshot(org.openqa.selenium.WebDriver, String)}
221     *             instead.
222     */
223    @Deprecated
224    public File takeScreenshot(String filename) {
225        ScreenshotTaker taker = new ScreenshotTaker();
226        return taker.takeScreenshot(driver, filename);
227    }
228
229}