001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jmeter.assertions;
020:
021: import java.io.ByteArrayInputStream;
022: import java.io.ByteArrayOutputStream;
023: import java.io.FileWriter;
024: import java.io.IOException;
025: import java.io.PrintWriter;
026: import java.io.Serializable;
027: import java.io.StringWriter;
028: import java.text.MessageFormat;
029:
030: import org.apache.jmeter.samplers.SampleResult;
031: import org.apache.jmeter.testelement.AbstractTestElement;
032: import org.apache.jmeter.testelement.property.BooleanProperty;
033: import org.apache.jmeter.testelement.property.LongProperty;
034: import org.apache.jmeter.testelement.property.StringProperty;
035: import org.apache.jmeter.util.JMeterUtils;
036: import org.apache.jorphan.logging.LoggingManager;
037: import org.apache.log.Logger;
038: import org.w3c.tidy.Node;
039: import org.w3c.tidy.Tidy;
040:
041: /**
042: * Assertion to validate the response of a Sample with Tidy.
043: */
044: public class HTMLAssertion extends AbstractTestElement implements
045: Serializable, Assertion {
046:
047: private static final Logger log = LoggingManager
048: .getLoggerForClass();
049:
050: public static final String DEFAULT_DOCTYPE = "omit"; //$NON-NLS-1$
051:
052: public static final String DOCTYPE_KEY = "html_assertion_doctype"; //$NON-NLS-1$
053:
054: public static final String ERRORS_ONLY_KEY = "html_assertion_errorsonly"; //$NON-NLS-1$
055:
056: public static final String ERROR_THRESHOLD_KEY = "html_assertion_error_threshold"; //$NON-NLS-1$
057:
058: public static final String WARNING_THRESHOLD_KEY = "html_assertion_warning_threshold"; //$NON-NLS-1$
059:
060: public static final String FORMAT_KEY = "html_assertion_format"; //$NON-NLS-1$
061:
062: public static final String FILENAME_KEY = "html_assertion_filename"; //$NON-NLS-1$
063:
064: /**
065: *
066: */
067: public HTMLAssertion() {
068: log.debug("HTMLAssertion(): called");
069: }
070:
071: /**
072: * Returns the result of the Assertion. If so an AssertionResult containing
073: * a FailureMessage will be returned. Otherwise the returned AssertionResult
074: * will reflect the success of the Sample.
075: */
076: public AssertionResult getResult(SampleResult inResponse) {
077: log.debug("HTMLAssertions.getResult() called");
078:
079: // no error as default
080: AssertionResult result = new AssertionResult(getName());
081:
082: if (inResponse.getResponseData().length == 0) {
083: return result.setResultForNull();
084: }
085:
086: result.setFailure(false);
087:
088: // create parser
089: Tidy tidy = null;
090: try {
091: log.debug("HTMLAssertions.getResult(): Setup tidy ...");
092: log.debug("doctype: " + getDoctype());
093: log.debug("errors only: " + isErrorsOnly());
094: log.debug("error threshold: " + getErrorThreshold());
095: log.debug("warning threshold: " + getWarningThreshold());
096: log.debug("html mode: " + isHTML());
097: log.debug("xhtml mode: " + isXHTML());
098: log.debug("xml mode: " + isXML());
099: tidy = new Tidy();
100: tidy.setCharEncoding(org.w3c.tidy.Configuration.UTF8);
101: tidy.setQuiet(false);
102: tidy.setShowWarnings(true);
103: tidy.setOnlyErrors(isErrorsOnly());
104: tidy.setDocType(getDoctype());
105: if (isXHTML()) {
106: tidy.setXHTML(true);
107: } else if (isXML()) {
108: tidy.setXmlTags(true);
109: }
110: log.debug("err file: " + getFilename());
111: tidy.setErrfile(getFilename());
112:
113: if (log.isDebugEnabled()) {
114: log.debug("getParser : tidy parser created - " + tidy);
115: }
116: log
117: .debug("HTMLAssertions.getResult(): Tidy instance created!");
118:
119: } catch (Exception e) {//TODO replace with proper Exception
120: log.error("Unable to instantiate tidy parser", e);
121: result.setFailure(true);
122: result
123: .setFailureMessage("Unable to instantiate tidy parser");
124: // return with an error
125: return result;
126: }
127:
128: /*
129: * Run tidy.
130: */
131: try {
132: log
133: .debug("HTMLAssertions.getResult(): start parsing with tidy ...");
134:
135: StringWriter errbuf = new StringWriter();
136: tidy.setErrout(new PrintWriter(errbuf));
137: // Node node = tidy.parseDOM(new
138: // ByteArrayInputStream(response.getResponseData()), null);
139: ByteArrayOutputStream os = new ByteArrayOutputStream();
140: log.debug("Start : parse");
141: Node node = tidy.parse(new ByteArrayInputStream(inResponse
142: .getResponseData()), os);
143: if (log.isDebugEnabled()) {
144: log.debug("node : " + node);
145: }
146: log.debug("End : parse");
147: log
148: .debug("HTMLAssertions.getResult(): parsing with tidy done!");
149: log.debug("Output: " + os.toString());
150:
151: // write output to file
152: writeOutput(errbuf.toString());
153:
154: // evaluate result
155: if ((tidy.getParseErrors() > getErrorThreshold())
156: || (!isErrorsOnly() && (tidy.getParseWarnings() > getWarningThreshold()))) {
157: log
158: .debug("HTMLAssertions.getResult(): errors/warnings detected:");
159: log.debug(errbuf.toString());
160: result.setFailure(true);
161: result.setFailureMessage(MessageFormat.format(
162: "Tidy Parser errors: "
163: + tidy.getParseErrors() + " (allowed "
164: + getErrorThreshold() + ") "
165: + "Tidy Parser warnings: "
166: + tidy.getParseWarnings()
167: + " (allowed " + getWarningThreshold()
168: + ")", new Object[0]));
169: // return with an error
170:
171: } else if ((tidy.getParseErrors() > 0)
172: || (tidy.getParseWarnings() > 0)) {
173: // return with no error
174: log
175: .debug("HTMLAssertions.getResult(): there were errors/warnings but threshold to high");
176: result.setFailure(false);
177: } else {
178: // return with no error
179: log
180: .debug("HTMLAssertions.getResult(): no errors/warnings detected:");
181: result.setFailure(false);
182: }
183:
184: } catch (Exception e) {//TODO replace with proper Exception
185: // return with an error
186: log.warn("Cannot parse result content", e);
187: result.setFailure(true);
188: result.setFailureMessage(e.getMessage());
189: }
190: return result;
191: }
192:
193: /**
194: * Writes the output of tidy to file.
195: *
196: * @param inOutput
197: */
198: private void writeOutput(String inOutput) {
199: String lFilename = getFilename();
200:
201: // check if filename defined
202: if ((lFilename != null) && (!"".equals(lFilename.trim()))) {
203: FileWriter lOutputWriter = null;
204: try {
205:
206: // open file
207: lOutputWriter = new FileWriter(lFilename, false);
208:
209: // write to file
210: lOutputWriter.write(inOutput);
211:
212: // flush
213: lOutputWriter.flush();
214:
215: log
216: .debug("writeOutput() -> output successfully written to file "
217: + lFilename);
218:
219: } catch (IOException ex) {
220: log.warn(
221: "writeOutput() -> could not write output to file "
222: + lFilename, ex);
223: } finally {
224: // close file
225: if (lOutputWriter != null) {
226: try {
227: lOutputWriter.close();
228: } catch (IOException e) {
229: }
230: }
231: }
232: }
233: }
234:
235: /**
236: * Gets the doctype
237: *
238: * @return the documemt type
239: */
240: public String getDoctype() {
241: return getPropertyAsString(DOCTYPE_KEY);
242: }
243:
244: /**
245: * Check if errors will be reported only
246: *
247: * @return boolean - report errors only?
248: */
249: public boolean isErrorsOnly() {
250: return getPropertyAsBoolean(ERRORS_ONLY_KEY);
251: }
252:
253: /**
254: * Gets the threshold setting for errors
255: *
256: * @return long error threshold
257: */
258: public long getErrorThreshold() {
259: return getPropertyAsLong(ERROR_THRESHOLD_KEY);
260: }
261:
262: /**
263: * Gets the threshold setting for warnings
264: *
265: * @return long warning threshold
266: */
267: public long getWarningThreshold() {
268: return getPropertyAsLong(WARNING_THRESHOLD_KEY);
269: }
270:
271: /**
272: * Sets the doctype setting
273: *
274: * @param inDoctype
275: */
276: public void setDoctype(String inDoctype) {
277: if ((inDoctype == null) || (inDoctype.trim().equals(""))) {
278: setProperty(new StringProperty(DOCTYPE_KEY, DEFAULT_DOCTYPE));
279: } else {
280: setProperty(new StringProperty(DOCTYPE_KEY, inDoctype));
281: }
282: }
283:
284: /**
285: * Sets if errors shoud be tracked only
286: *
287: * @param inErrorsOnly
288: */
289: public void setErrorsOnly(boolean inErrorsOnly) {
290: setProperty(new BooleanProperty(ERRORS_ONLY_KEY, inErrorsOnly));
291: }
292:
293: /**
294: * Sets the threshold on error level
295: *
296: * @param inErrorThreshold
297: */
298: public void setErrorThreshold(long inErrorThreshold) {
299: if (inErrorThreshold < 0L) {
300: throw new IllegalArgumentException(JMeterUtils
301: .getResString("argument_must_not_be_negative")); //$NON-NLS-1$
302: }
303: if (inErrorThreshold == Long.MAX_VALUE) {
304: setProperty(new LongProperty(ERROR_THRESHOLD_KEY, 0));
305: } else {
306: setProperty(new LongProperty(ERROR_THRESHOLD_KEY,
307: inErrorThreshold));
308: }
309: }
310:
311: /**
312: * Sets the threshold on warning level
313: *
314: * @param inWarningThreshold
315: */
316: public void setWarningThreshold(long inWarningThreshold) {
317: if (inWarningThreshold < 0L) {
318: throw new IllegalArgumentException(JMeterUtils
319: .getResString("argument_must_not_be_negative")); //$NON-NLS-1$
320: }
321: if (inWarningThreshold == Long.MAX_VALUE) {
322: setProperty(new LongProperty(WARNING_THRESHOLD_KEY, 0));
323: } else {
324: setProperty(new LongProperty(WARNING_THRESHOLD_KEY,
325: inWarningThreshold));
326: }
327: }
328:
329: /**
330: * Enables html validation mode
331: */
332: public void setHTML() {
333: setProperty(new LongProperty(FORMAT_KEY, 0));
334: }
335:
336: /**
337: * Check if html validation mode is set
338: *
339: * @return boolean
340: */
341: public boolean isHTML() {
342: return getPropertyAsLong(FORMAT_KEY) == 0;
343: }
344:
345: /**
346: * Enables xhtml validation mode
347: */
348: public void setXHTML() {
349: setProperty(new LongProperty(FORMAT_KEY, 1));
350: }
351:
352: /**
353: * Check if xhtml validation mode is set
354: *
355: * @return boolean
356: */
357: public boolean isXHTML() {
358: return getPropertyAsLong(FORMAT_KEY) == 1;
359: }
360:
361: /**
362: * Enables xml validation mode
363: */
364: public void setXML() {
365: setProperty(new LongProperty(FORMAT_KEY, 2));
366: }
367:
368: /**
369: * Check if xml validation mode is set
370: *
371: * @return boolean
372: */
373: public boolean isXML() {
374: return getPropertyAsLong(FORMAT_KEY) == 2;
375: }
376:
377: /**
378: * Sets the name of the file where tidy writes the output to
379: *
380: * @return name of file
381: */
382: public String getFilename() {
383: return getPropertyAsString(FILENAME_KEY);
384: }
385:
386: /**
387: * Sets the name of the tidy output file
388: *
389: * @param inName
390: */
391: public void setFilename(String inName) {
392: setProperty(FILENAME_KEY, inName);
393: }
394: }
|