sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From chet...@apache.org
Subject svn commit: r1651751 - in /sling/trunk: bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/ launchpad/integration-tests/ launchpad/integration-tests/src/main/resources/ testing/junit/core/ testing/junit/core/src/main/jav...
Date Wed, 14 Jan 2015 17:54:31 GMT
Author: chetanm
Date: Wed Jan 14 17:54:31 2015
New Revision: 1651751

URL: http://svn.apache.org/r1651751
Log:
SLING-4280 - Enable dumping of remote server logs in case of test failures

- Commons Testing - Added a new class TestInfoPassingClient which passes Sling test related
headers (MDC keys starting with X-Sling) to outgoing request. Also used by HttpTestBase
 - Junit Core - Exposed a TestLogServlet to capture server side logs for test being executed
 - Testing Tools - Added RemoteLogDumper rule which connects to remote server and obtains
the server logs and then dump them to system err stream upon failure. Also added a TestDescriptionInterceptor
 - integration-test - Modified the dependency to use logback as it supports MDC

Added:
    sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/TestInfoPassingClient.java
  (with props)
    sling/trunk/launchpad/integration-tests/src/main/resources/logback-test.xml   (with props)
    sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/impl/servlet/TestLogServlet.java
  (with props)
    sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/RemoteLogDumper.java
  (with props)
Modified:
    sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/HttpTestBase.java
    sling/trunk/launchpad/integration-tests/pom.xml
    sling/trunk/testing/junit/core/pom.xml
    sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestDescriptionInterceptor.java
    sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestDescriptionRule.java
    sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestNameLoggingFilter.java
    sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/package-info.java
    sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/SlingTestBase.java

Modified: sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/HttpTestBase.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/HttpTestBase.java?rev=1651751&r1=1651750&r2=1651751&view=diff
==============================================================================
--- sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/HttpTestBase.java
(original)
+++ sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/HttpTestBase.java
Wed Jan 14 17:54:31 2015
@@ -109,7 +109,7 @@ public class HttpTestBase extends TestCa
         public TestNode(String parentPath, Map<String, String> properties) throws IOException
{
             super(testClient, parentPath, properties);
         }
-    };
+    }
 
     public static String removeEndingSlash(String str) {
         if(str != null && str.endsWith("/")) {
@@ -153,7 +153,7 @@ public class HttpTestBase extends TestCa
         }
 
         // setup HTTP client, with authentication (using default Jackrabbit credentials)
-        httpClient = new HttpClient();
+        httpClient = new TestInfoPassingClient();
         httpClient.getParams().setAuthenticationPreemptive(true);
         Credentials defaultcreds = getDefaultCredentials();
         httpClient.getState().setCredentials(new AuthScope(url.getHost(), url.getPort(),
AuthScope.ANY_REALM), defaultcreds);

Added: sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/TestInfoPassingClient.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/TestInfoPassingClient.java?rev=1651751&view=auto
==============================================================================
--- sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/TestInfoPassingClient.java
(added)
+++ sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/TestInfoPassingClient.java
Wed Jan 14 17:54:31 2015
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.commons.testing.integration;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.httpclient.HostConfiguration;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpState;
+import org.slf4j.MDC;
+
+/**
+ * HttpClient extension which also passes test related headers as part
+ * of outgoing HTTP request
+ */
+public class TestInfoPassingClient extends HttpClient {
+    //Defined in org.apache.sling.testing.tools.junit.TestLogRule
+    private static final String SLING_HEADER_PREFIX = "X-Sling-";
+
+    @Override
+    public int executeMethod(HostConfiguration hostconfig, HttpMethod method,
+                             HttpState state) throws IOException {
+        addSlingHeaders(method);
+        return super.executeMethod(hostconfig, method, state);
+    }
+
+    /**
+     * Adds all MDC key-value pairs as HTTP header where the key starts
+     * with 'X-Sling-'
+     */
+    private static void addSlingHeaders(HttpMethod m){
+        Map<?,?> mdc = MDC.getCopyOfContextMap();
+        if (mdc != null) {
+            for (Map.Entry<?, ?> e : mdc.entrySet()) {
+                Object key = e.getKey();
+                if (key instanceof String
+                        && ((String)key).startsWith(SLING_HEADER_PREFIX)
+                        && e.getValue() instanceof String) {
+                    m.addRequestHeader((String) key, (String) e.getValue());
+                }
+            }
+        }
+    }
+}

Propchange: sling/trunk/bundles/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/TestInfoPassingClient.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: sling/trunk/launchpad/integration-tests/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/integration-tests/pom.xml?rev=1651751&r1=1651750&r2=1651751&view=diff
==============================================================================
--- sling/trunk/launchpad/integration-tests/pom.xml (original)
+++ sling/trunk/launchpad/integration-tests/pom.xml Wed Jan 14 17:54:31 2015
@@ -150,6 +150,24 @@
 
   <dependencies>
     <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.6</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>jcl-over-slf4j</artifactId>
+      <version>1.7.6</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <version>1.1.2</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
         <groupId>org.apache.felix</groupId>
         <artifactId>org.apache.felix.scr.annotations</artifactId>
     </dependency>
@@ -173,7 +191,7 @@
     <dependency>
       <groupId>org.apache.sling</groupId>
       <artifactId>org.apache.sling.commons.testing</artifactId>
-      <version>2.0.15-SNAPSHOT</version>
+      <version>2.0.17-SNAPSHOT</version>
       <exclusions>
         <exclusion>
           <groupId>org.slf4j</groupId>
@@ -230,11 +248,6 @@
       <scope>compile</scope>
     </dependency>
     <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-simple</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>xmlunit</groupId>
       <artifactId>xmlunit</artifactId>
       <version>1.3</version>
@@ -265,7 +278,7 @@
     <dependency>
       <groupId>org.apache.sling</groupId>
       <artifactId>org.apache.sling.testing.tools</artifactId>
-      <version>1.0.6</version>
+      <version>1.0.9-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>org.apache.sling</groupId>

Added: sling/trunk/launchpad/integration-tests/src/main/resources/logback-test.xml
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/integration-tests/src/main/resources/logback-test.xml?rev=1651751&view=auto
==============================================================================
--- sling/trunk/launchpad/integration-tests/src/main/resources/logback-test.xml (added)
+++ sling/trunk/launchpad/integration-tests/src/main/resources/logback-test.xml Wed Jan 14
17:54:31 2015
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<configuration>
+
+    <appender name="file" class="ch.qos.logback.core.FileAppender">
+        <file>target/sling-tests.log</file>
+        <encoder>
+            <pattern>%date{HH:mm:ss.SSS} %-5level %-40([%thread] %F:%L) %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="sift" class="ch.qos.logback.classic.sift.SiftingAppender">
+        <discriminator>
+            <Key>testclass</Key>
+            <DefaultValue>junit</DefaultValue>
+        </discriminator>
+        <sift>
+            <appender name="FILE-${testname}" class="ch.qos.logback.core.FileAppender">
+                <File>target/surefire-reports/${testclass}.log</File>
+                <layout class="ch.qos.logback.classic.PatternLayout">
+                    <Pattern>%date{HH:mm:ss.SSS} %-5level %-40([%thread] %F:%L) %msg%n</Pattern>
+                </layout>
+            </appender>
+        </sift>
+    </appender>
+
+    <root level="INFO">
+        <appender-ref ref="file" />
+        <appender-ref ref="sift" />
+    </root>
+
+</configuration>
\ No newline at end of file

Propchange: sling/trunk/launchpad/integration-tests/src/main/resources/logback-test.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: sling/trunk/testing/junit/core/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/testing/junit/core/pom.xml?rev=1651751&r1=1651750&r2=1651751&view=diff
==============================================================================
--- sling/trunk/testing/junit/core/pom.xml (original)
+++ sling/trunk/testing/junit/core/pom.xml Wed Jan 14 17:54:31 2015
@@ -160,11 +160,23 @@
             <version>${jacoco.version}</version>
             <scope>provided</scope>
         </dependency>
- 		<dependency>
+ 		    <dependency>
             <groupId>org.hamcrest</groupId>
             <artifactId>hamcrest-core</artifactId>
             <version>${hamcrest.version}</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.0.13</version>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.2.2</version>
+        </dependency>
     </dependencies>
 </project>

Added: sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/impl/servlet/TestLogServlet.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/impl/servlet/TestLogServlet.java?rev=1651751&view=auto
==============================================================================
--- sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/impl/servlet/TestLogServlet.java
(added)
+++ sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/impl/servlet/TestLogServlet.java
Wed Jan 14 17:54:31 2015
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.junit.impl.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Layout;
+import ch.qos.logback.core.read.CyclicBufferAppender;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.junit.runner.Description;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+@Component(immediate=true, metatype=true,
+        label = "Apache Sling Test Log Collector",
+        description = "Servlet that exposes logs collected for a particular test execution"
+)
+public class TestLogServlet extends HttpServlet {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    //These name should be kept in sync with
+    // org.apache.sling.testing.tools.junit.RemoteLogDumper
+    public static final String TEST_NAME = "X-Sling-Test-Name";
+    public static final String TEST_CLASS = "X-Sling-Test-Class";
+
+    @Property(value="/system/sling/testlog")
+    static final String SERVLET_PATH_NAME = "servlet.path";
+
+    static final int DEFAULT_SIZE = 1000;
+    @Property(intValue = DEFAULT_SIZE,
+            label = "Log Buffer Size",
+            description = "Size of in memory log buffer. Only recent logs upto buffer size
would be retained"
+    )
+    static final String LOG_BUFFER_SIZE = "log.buffer.size";
+
+    private static final String DEFAULT_PATTERN = "%d{dd.MM.yyyy HH:mm:ss.SSS} *%level* [%thread]
%logger %msg%n";
+
+    @Property(label = "Log Pattern",
+            description = "Message Pattern for formatting the log messages",
+            value = DEFAULT_PATTERN
+    )
+    private static final String PROP_MSG_PATTERN = "logPattern";
+
+    /** Non-null if we are registered with HttpService */
+    private String servletPath;
+
+    @Reference
+    private HttpService httpService;
+
+    private CyclicBufferAppender<ILoggingEvent> appender;
+
+    private Layout<ILoggingEvent> layout;
+
+    private ServiceRegistration filter;
+
+    private volatile Description currentTest;
+
+    @Activate
+    protected void activate(BundleContext ctx, Map<String, ?> config) throws Exception
{
+        registerServlet(config);
+        registerAppender(config);
+        registerFilter(ctx);
+        createLayout(config);
+    }
+
+    @Deactivate
+    protected void deactivate() throws Exception {
+        deregisterFilter();
+        deregisterServlet();
+        deregisterAppender();
+        stopLayout();
+    }
+
+    public void testRunStarted(Description description) {
+        if (description != null && !description.equals(currentTest)){
+            currentTest = description;
+            resetAppender();
+            log.info("Starting test execution {}", description);
+        }
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException {
+        final PrintWriter pw = response.getWriter();
+        final String className = request.getParameter(TEST_CLASS);
+        final String testName = request.getParameter(TEST_NAME);
+
+        //If className and testName explicitly specified check if the logs
+        //are being collected for expected test
+        if (className != null && testName != null){
+            Description expected = Description.createTestDescription(className, testName);
+
+            if (!expected.equals(currentTest)){
+                pw.printf("Test name mismatch : Current test [%s], Expected test [%s]%n",
currentTest, expected);
+                return;
+            }
+        }
+
+        //Detach the appender so that we can extract its content safely
+        rootLogger().detachAppender(appender);
+        try {
+            for (int i = 0; i < appender.getLength(); i++) {
+                pw.print(layout.doLayout(appender.get(i)));
+            }
+            resetAppender();
+        } finally {
+            rootLogger().addAppender(appender);
+        }
+    }
+
+    private void resetAppender() {
+        synchronized (appender) {
+            appender.reset();
+        }
+    }
+
+    private void registerAppender(Map<String, ?> config) {
+        int size = PropertiesUtil.toInteger(config.get(LOG_BUFFER_SIZE), DEFAULT_SIZE);
+        appender = new CyclicBufferAppender<ILoggingEvent>();
+        appender.setMaxSize(size);
+        appender.setContext(getContext());
+        appender.setName("TestLogCollector");
+        appender.start();
+        rootLogger().addAppender(appender);
+    }
+
+    private void deregisterAppender() {
+        if (appender != null) {
+            rootLogger().detachAppender(appender);
+            appender.stop();
+            appender = null;
+        }
+    }
+
+    private void createLayout(Map<String, ?> config) {
+        String pattern = PropertiesUtil.toString(config.get(PROP_MSG_PATTERN), DEFAULT_PATTERN);
+        PatternLayout pl = new PatternLayout();
+        pl.setPattern(pattern);
+        pl.setOutputPatternAsHeader(false);
+        pl.setContext(getContext());
+        pl.start();
+
+        layout = pl;
+    }
+
+    private void stopLayout() {
+        if (layout != null){
+            layout.stop();
+        }
+    }
+
+    private void registerServlet(Map<String, ?> config) throws ServletException, NamespaceException
{
+        servletPath = getServletPath(config);
+        if(servletPath == null) {
+            log.info("Servlet path is null, not registering with HttpService");
+        } else {
+            httpService.registerServlet(servletPath, this, null, null);
+            log.info("Servlet registered at {}", servletPath);
+        }
+    }
+
+    private void deregisterServlet() {
+        if(servletPath != null) {
+            httpService.unregister(servletPath);
+            log.info("Servlet unregistered from path {}", servletPath);
+        }
+        servletPath = null;
+    }
+
+    private void registerFilter(BundleContext ctx) {
+        Properties props = new Properties();
+        props.put(Constants.SERVICE_DESCRIPTION, "Filter to extract testName from request
headers");
+        props.put(Constants.SERVICE_VENDOR, ctx.getBundle().getHeaders().get(Constants.BUNDLE_VENDOR));
+
+        props.put("pattern", "/.*");
+        filter = ctx.registerService(Filter.class.getName(), new TestNameLoggingFilter(),
props);
+    }
+
+    private void deregisterFilter() {
+        if (filter != null) {
+            filter.unregister();
+        }
+    }
+
+    private class TestNameLoggingFilter implements Filter {
+
+        public void init(FilterConfig filterConfig) throws ServletException {
+
+        }
+
+        public void doFilter(ServletRequest request, ServletResponse response,
+                             FilterChain chain) throws IOException, ServletException {
+
+            final HttpServletRequest httpRequest = (HttpServletRequest) request;
+            final String className = httpRequest.getHeader(TEST_CLASS);
+            final String testName = httpRequest.getHeader(TEST_NAME);
+
+            if (className == null || testName == null) {
+                chain.doFilter(request, response);
+                return;
+            }
+
+            try {
+                MDC.put(TEST_NAME, testName);
+                MDC.put(TEST_CLASS, className);
+
+                testRunStarted(Description.createTestDescription(className, testName));
+
+                chain.doFilter(request, response);
+            } finally {
+
+                MDC.remove(TEST_NAME);
+                MDC.remove(TEST_CLASS);
+            }
+
+        }
+
+        public void destroy() {
+
+        }
+    }
+
+    //~------------------------------------------------< utility >
+
+    /**
+     * Return the path at which to mount this servlet, or null
+     * if it must not be mounted.
+     * @param ctx
+     */
+    private static String getServletPath(Map<String, ?> config) {
+        String result = (String)config.get(SERVLET_PATH_NAME);
+        if(result != null && result.trim().length() == 0) {
+            result = null;
+        }
+        return result;
+    }
+
+    private static LoggerContext getContext(){
+        return (LoggerContext) LoggerFactory.getILoggerFactory();
+    }
+
+    private static ch.qos.logback.classic.Logger rootLogger() {
+        return getContext().getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
+    }
+}

Propchange: sling/trunk/testing/junit/core/src/main/java/org/apache/sling/junit/impl/servlet/TestLogServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/RemoteLogDumper.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/RemoteLogDumper.java?rev=1651751&view=auto
==============================================================================
--- sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/RemoteLogDumper.java
(added)
+++ sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/RemoteLogDumper.java
Wed Jan 14 17:54:31 2015
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.testing.tools.junit;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.sling.testing.tools.http.Request;
+import org.apache.sling.testing.tools.http.RequestBuilder;
+import org.apache.sling.testing.tools.http.RequestExecutor;
+import org.apache.sling.testing.tools.sling.SlingInstanceState;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.slf4j.MDC;
+
+/**
+ * The RemoteLogDumper Rule fetches logs which are generated due to execution of test from
the
+ * remote server and dumps them locally upon test failure. This simplifies determining failure
+ * cause by providing all required data locally. This would be specially useful when running
test
+ * in CI server where server logs gets cluttered with all other test executions
+ *
+ * <pre>
+ *     public class LoginTestIT {
+ *
+ *     &#064;Rule
+ *     public TestRule logDumper = new RemoteLogDumper();
+ *
+ *     &#064;Test
+ *     public void remoteLogin() {
+ *          //Make calls to remote server
+ *          assertEquals(&quot;testA&quot;, name.getMethodName());
+ *     }
+ *
+ *     }
+ * </pre>
+ */
+public class RemoteLogDumper extends TestWatcher {
+    public static final String TEST_CLASS = "X-Sling-Test-Class";
+    public static final String TEST_NAME = "X-Sling-Test-Name";
+    /**
+     * Path for the org.apache.sling.junit.impl.servlet.TestLogServlet
+     */
+    static final String SERVLET_PATH = "/system/sling/testlog";
+
+    @Override
+    protected void finished(Description description) {
+        MDC.remove(TEST_CLASS);
+        MDC.remove(TEST_NAME);
+    }
+
+    @Override
+    protected void starting(Description description) {
+        MDC.put(TEST_CLASS, description.getClassName());
+        MDC.put(TEST_NAME, description.getMethodName());
+    }
+
+    @Override
+    protected void failed(Throwable e, Description description) {
+        final String baseUrl = getServerBaseUrl();
+        final StringWriter sw = new StringWriter();
+        final PrintWriter pw = new PrintWriter(sw);
+
+        if (baseUrl != null) {
+            try {
+                DefaultHttpClient httpClient = new DefaultHttpClient();
+                RequestExecutor executor = new RequestExecutor(httpClient);
+                RequestBuilder rb = new RequestBuilder(baseUrl);
+
+                Request r = rb.buildGetRequest(SERVLET_PATH,
+                        TEST_CLASS, description.getClassName(),
+                        TEST_NAME, description.getMethodName());
+
+                executor.execute(r);
+                int statusCode = executor.getResponse().getStatusLine().getStatusCode();
+
+                String msg = e.getMessage();
+                if (msg != null) {
+                    pw.println(msg);
+                }
+
+                if (statusCode == 200){
+                    pw.printf("=============== Logs from server [%s] for [%s]===================%n",
+                            baseUrl, description.getMethodName());
+                    pw.print(executor.getContent());
+                    pw.println("========================================================");
+                } else {
+                    pw.printf("Not able to fetch logs from [%s%s]. " +
+                            "TestLogServer probably not configured %n", baseUrl, SERVLET_PATH);
+                }
+
+            } catch (Throwable t) {
+                System.err.printf("Error occurred while fetching test logs from server [%s]
%n", baseUrl);
+                t.printStackTrace(System.err);
+            }
+
+            System.err.print(sw.toString());
+        }
+    }
+
+    private static String getServerBaseUrl() {
+        SlingInstanceState testState = SlingInstanceState.getInstance(SlingInstanceState.DEFAULT_INSTANCE_NAME);
+        String baseUrl = testState.getServerBaseUrl();
+        if (testState.isServerReady()) {
+            return baseUrl;
+        } else if (baseUrl == null) {
+            //Running via older HttpTestBase
+            baseUrl = removeEndingSlash(System.getProperty("launchpad.http.server.url"));
+        }
+
+        if (baseUrl == null){
+            baseUrl = "http://localhost:8888";
+        }
+        return baseUrl;
+    }
+
+    private static String removeEndingSlash(String str) {
+        if(str != null && str.endsWith("/")) {
+            return str.substring(0, str.length() - 1);
+        }
+        return str;
+    }
+}

Propchange: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/RemoteLogDumper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestDescriptionInterceptor.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestDescriptionInterceptor.java?rev=1651751&r1=1651750&r2=1651751&view=diff
==============================================================================
--- sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestDescriptionInterceptor.java
(original)
+++ sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestDescriptionInterceptor.java
Wed Jan 14 17:54:31 2015
@@ -16,13 +16,14 @@
  */
 package org.apache.sling.testing.tools.junit;
 
+import java.io.IOException;
+import java.util.Map;
+
 import org.apache.http.HttpException;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequestInterceptor;
 import org.apache.http.protocol.HttpContext;
-import org.junit.runner.Description;
-
-import java.io.IOException;
+import org.slf4j.MDC;
 
 /**
  * HttpClient interceptor that propagates the current test name as part HTTP request headers.
@@ -32,15 +33,28 @@ import java.io.IOException;
  *
  * @see MDC http://www.slf4j.org/manual.html
  */
-public class TestDescriptionInterceptor implements HttpRequestInterceptor{
-    public static final String TEST_NAME_HEADER = "sling.test.name";
-    public static final String TEST_CLASS_HEADER = "sling.test.class";
+public class TestDescriptionInterceptor implements HttpRequestInterceptor {
+    private static final String SLING_HEADER_PREFIX = "X-Sling-";
 
     public void process(HttpRequest httpRequest, HttpContext httpContext) throws HttpException,
IOException {
-       final Description desc = TestDescriptionRule.getCurrentTestDescription();
-        if(desc != null){
-            httpRequest.addHeader(TEST_NAME_HEADER,desc.getMethodName());
-            httpRequest.addHeader(TEST_CLASS_HEADER,desc.getClassName());
+        addSlingHeaders(httpRequest);
+    }
+
+    /**
+     * Adds all MDC key-value pairs as HTTP header where the key starts
+     * with 'X-Sling-'
+     */
+    private static void addSlingHeaders(HttpRequest m){
+        Map<?,?> mdc = MDC.getCopyOfContextMap();
+        if (mdc != null) {
+            for (Map.Entry<?, ?> e : mdc.entrySet()) {
+                Object key = e.getKey();
+                if (key instanceof String
+                        && ((String)key).startsWith(SLING_HEADER_PREFIX)
+                        && e.getValue() instanceof String) {
+                    m.addHeader((String)key, (String)e.getValue());
+                }
+            }
         }
     }
 }
\ No newline at end of file

Modified: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestDescriptionRule.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestDescriptionRule.java?rev=1651751&r1=1651750&r2=1651751&view=diff
==============================================================================
--- sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestDescriptionRule.java
(original)
+++ sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestDescriptionRule.java
Wed Jan 14 17:54:31 2015
@@ -18,25 +18,21 @@ package org.apache.sling.testing.tools.j
 
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
+import org.slf4j.MDC;
 
 /**
  * Junit rule which exposes the current executing test's description as a thread local instance
  */
 public class TestDescriptionRule extends TestWatcher {
-
-    private static final ThreadLocal<Description> currentTestDescription = new ThreadLocal<Description>();
-
-    @Override
-    protected void finished(Description description) {
-        currentTestDescription.remove();
-    }
-
     @Override
     protected void starting(Description description) {
-        currentTestDescription.set(description);
+        MDC.put(RemoteLogDumper.TEST_CLASS, description.getClassName());
+        MDC.put(RemoteLogDumper.TEST_NAME, description.getMethodName());
     }
 
-    public static Description getCurrentTestDescription(){
-        return currentTestDescription.get();
+    @Override
+    protected void finished(Description description) {
+        MDC.remove(RemoteLogDumper.TEST_CLASS);
+        MDC.remove(RemoteLogDumper.TEST_NAME);
     }
 }

Modified: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestNameLoggingFilter.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestNameLoggingFilter.java?rev=1651751&r1=1651750&r2=1651751&view=diff
==============================================================================
--- sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestNameLoggingFilter.java
(original)
+++ sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/TestNameLoggingFilter.java
Wed Jan 14 17:54:31 2015
@@ -16,8 +16,8 @@
  */
 package org.apache.sling.testing.tools.junit;
 
-import static org.apache.sling.testing.tools.junit.TestDescriptionInterceptor.TEST_CLASS_HEADER;
-import static org.apache.sling.testing.tools.junit.TestDescriptionInterceptor.TEST_NAME_HEADER;
+import static org.apache.sling.testing.tools.junit.RemoteLogDumper.TEST_CLASS;
+import static org.apache.sling.testing.tools.junit.RemoteLogDumper.TEST_NAME;
 
 import java.io.IOException;
 
@@ -54,17 +54,17 @@ public class TestNameLoggingFilter imple
     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain)
             throws IOException, ServletException {
         final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
-        final String className = httpRequest.getHeader(TEST_CLASS_HEADER);
+        final String className = httpRequest.getHeader(TEST_CLASS);
         
         if(className == null) {
             filterChain.doFilter(servletRequest,servletResponse);
             return;
         }
         
-        final String testName = httpRequest.getHeader(TEST_NAME_HEADER);
+        final String testName = httpRequest.getHeader(TEST_NAME);
         try {
-            MDC.put(TEST_NAME_HEADER,testName);
-            MDC.put(TEST_CLASS_HEADER,className);
+            MDC.put(TEST_NAME,testName);
+            MDC.put(TEST_CLASS,className);
 
             log.info("Starting request as part of test ==== {}.{} ====",className,testName);
 
@@ -73,8 +73,8 @@ public class TestNameLoggingFilter imple
         } finally {
             log.info("Finishing request as part of test ==== {}.{} ====",className,testName);
 
-            MDC.remove(TEST_NAME_HEADER);
-            MDC.remove(TEST_CLASS_HEADER);
+            MDC.remove(TEST_NAME);
+            MDC.remove(TEST_CLASS);
         }
     }
 

Modified: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/package-info.java?rev=1651751&r1=1651750&r2=1651751&view=diff
==============================================================================
--- sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/package-info.java
(original)
+++ sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/junit/package-info.java
Wed Jan 14 17:54:31 2015
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@Version("1.0.9")
+@Version("2.0.0")
 package org.apache.sling.testing.tools.junit;
 
 import aQute.bnd.annotation.Version;

Modified: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/SlingTestBase.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/SlingTestBase.java?rev=1651751&r1=1651750&r2=1651751&view=diff
==============================================================================
--- sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/SlingTestBase.java
(original)
+++ sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/SlingTestBase.java
Wed Jan 14 17:54:31 2015
@@ -31,6 +31,7 @@ import org.apache.http.impl.client.Defau
 import org.apache.sling.testing.tools.http.RequestBuilder;
 import org.apache.sling.testing.tools.http.RequestExecutor;
 import org.apache.sling.testing.tools.jarexec.JarExecutor;
+import org.apache.sling.testing.tools.junit.TestDescriptionInterceptor;
 import org.apache.sling.testing.tools.osgi.WebconsoleClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -78,6 +79,7 @@ public class SlingTestBase implements Sl
         this.slingTestState = slingTestState;
         this.systemProperties = systemProperties;
         this.keepJarRunning = "true".equals(systemProperties.getProperty(KEEP_JAR_RUNNING_PROP));
+        this.httpClient.addRequestInterceptor(new TestDescriptionInterceptor());
 
 
         final String configuredUrl = systemProperties.getProperty(TEST_SERVER_URL_PROP, systemProperties.getProperty("launchpad.http.server.url"));



Mime
View raw message