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