View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit;
16  
17  import java.io.ByteArrayOutputStream;
18  import java.io.OutputStream;
19  import java.io.PrintStream;
20  import java.util.regex.Pattern;
21  
22  import org.apache.commons.lang3.StringUtils;
23  import org.junit.rules.MethodRule;
24  import org.junit.runners.model.FrameworkMethod;
25  import org.junit.runners.model.Statement;
26  
27  /**
28   * JUnit 4 {@link org.junit.Rule} verifying that nothing is printed to {@link System#err}
29   * during test execution. If this is the case, the rule generates a failure for
30   * the unit test.
31   *
32   * @author Marc Guillemot
33   * @author Ronald Brill
34   * @author Ahmed Ashour
35   * @author Frank Danek
36   */
37  public class ErrorOutputChecker implements MethodRule {
38      private PrintStream originalErr_;
39      private final ByteArrayOutputStream baos_ = new ByteArrayOutputStream();
40      private static final Pattern[] PATTERNS = {
41              // jetty
42              Pattern.compile(".*Logging initialized .* to org.eclipse.jetty.util.log.StdErrLog.*\r?\n"),
43  
44              // slf4j
45              Pattern.compile("SLF4J\\(I\\): .*\r?\n"),
46  
47              // Quercus
48              Pattern.compile(".*com.caucho.quercus.servlet.QuercusServlet initImpl\r?\n"),
49              Pattern.compile(".*QuercusServlet starting as QuercusServletImpl\r?\n"),
50              Pattern.compile(".*Quercus finished initialization in \\d*ms\r?\n"),
51  
52              // Xalan
53              Pattern.compile("ERROR:\\s*'Use of the extension function "
54                      + "'http://xml\\.apache\\.org/xalan/java/.*' "
55                      + "is not allowed when the secure processing feature is set to true\\.'\r?\n"),
56      };
57  
58      /**
59       * {@inheritDoc}
60       */
61      @Override
62      public Statement apply(final Statement base, final FrameworkMethod method, final Object target) {
63          if (target instanceof WebDriverTestCase) {
64              final WebDriverTestCase testCase = (WebDriverTestCase) target;
65              if (testCase.useRealBrowser()) {
66                  return base;
67              }
68          }
69  
70          return new Statement() {
71              @Override
72              public void evaluate() throws Throwable {
73                  wrapSystemErr();
74                  try {
75                      base.evaluate();
76                      verifyNoOutput();
77                  }
78                  finally {
79                      restoreSystemErr();
80                  }
81              }
82          };
83      }
84  
85      void verifyNoOutput() {
86          if (baos_.size() != 0) {
87              String output = baos_.toString();
88  
89              // remove webdriver messages
90              for (final Pattern pattern : PATTERNS) {
91                  output = pattern.matcher(output).replaceAll("");
92              }
93  
94              if (!output.isEmpty()) {
95                  if (output.contains("ChromeDriver")) {
96                      throw new RuntimeException("Outdated Chrome driver version: " + output);
97                  }
98                  if (output.contains("geckodriver")) {
99                      throw new RuntimeException("Outdated Gecko driver version: " + output);
100                 }
101                 output = StringUtils.replaceEach(output, new String[] {"\n", "\r"}, new String[]{"\\n", "\\r"});
102                 throw new RuntimeException("Test has produced output to System.err: " + output);
103             }
104         }
105     }
106 
107     private void wrapSystemErr() {
108         originalErr_ = System.err;
109         System.setErr(new NSAPrintStreamWrapper(originalErr_, baos_));
110     }
111 
112     void restoreSystemErr() {
113         System.setErr(originalErr_);
114     }
115 }
116 
117 /**
118  * A {@link PrintStream} spying what is written on the wrapped stream.
119  * It prints the content to the wrapped {@link PrintStream} and captures it simultaneously.
120  * @author Marc Guillemot
121  */
122 class NSAPrintStreamWrapper extends PrintStream {
123     private PrintStream wrapped_;
124 
125     NSAPrintStreamWrapper(final PrintStream original, final OutputStream spyOut) {
126         super(spyOut, true);
127         wrapped_ = original;
128     }
129 
130     /**
131      * {@inheritDoc}
132      */
133     @Override
134     public int hashCode() {
135         return wrapped_.hashCode();
136     }
137 
138     /**
139      * {@inheritDoc}
140      */
141     @Override
142     public boolean equals(final Object obj) {
143         return wrapped_.equals(obj);
144     }
145 
146     /**
147      * {@inheritDoc}
148      */
149     @Override
150     public String toString() {
151         return wrapped_.toString();
152     }
153 
154     /**
155      * {@inheritDoc}
156      */
157     @Override
158     public void flush() {
159         super.flush();
160         wrapped_.flush();
161     }
162 
163     /**
164      * {@inheritDoc}
165      */
166     @Override
167     public void close() {
168         super.close();
169         wrapped_.close();
170     }
171 
172     /**
173      * {@inheritDoc}
174      */
175     @Override
176     public boolean checkError() {
177         super.checkError();
178         return wrapped_.checkError();
179     }
180 
181     /**
182      * {@inheritDoc}
183      */
184     @Override
185     public void write(final int b) {
186         super.write(b);
187         wrapped_.write(b);
188     }
189 
190     /**
191      * {@inheritDoc}
192      */
193     @Override
194     public void write(final byte[] buf, final int off, final int len) {
195         super.write(buf, off, len);
196         wrapped_.write(buf, off, len);
197     }
198 }