1 15 package org.apache.tapestry.junit.mock; 16 17 import java.io.ByteArrayOutputStream ; 18 import java.io.FileInputStream ; 19 import java.io.FileNotFoundException ; 20 import java.io.IOException ; 21 import java.io.InputStream ; 22 import java.util.ArrayList ; 23 import java.util.HashMap ; 24 import java.util.List ; 25 import java.util.Locale ; 26 import java.util.Map ; 27 28 import javax.servlet.ServletException ; 29 import javax.servlet.http.Cookie ; 30 31 import junit.framework.AssertionFailedError; 32 import ognl.Ognl; 33 import ognl.OgnlException; 34 35 import org.apache.hivemind.ApplicationRuntimeException; 36 import org.apache.hivemind.HiveMind; 37 import org.apache.oro.text.regex.MalformedPatternException; 38 import org.apache.oro.text.regex.MatchResult; 39 import org.apache.oro.text.regex.Pattern; 40 import org.apache.oro.text.regex.PatternCompiler; 41 import org.apache.oro.text.regex.PatternMatcher; 42 import org.apache.oro.text.regex.PatternMatcherInput; 43 import org.apache.oro.text.regex.Perl5Compiler; 44 import org.apache.oro.text.regex.Perl5Matcher; 45 import org.apache.tapestry.ApplicationServlet; 46 import org.apache.tapestry.test.mock.InitParameterHolder; 47 import org.apache.tapestry.test.mock.MockContext; 48 import org.apache.tapestry.test.mock.MockRequest; 49 import org.apache.tapestry.test.mock.MockResponse; 50 import org.apache.tapestry.test.mock.MockServletConfig; 51 import org.apache.tapestry.util.xml.DocumentParseException; 52 import org.jdom.Document; 53 import org.jdom.Element; 54 import org.jdom.JDOMException; 55 import org.jdom.input.SAXBuilder; 56 57 68 69 public class MockTester 70 { 71 private String _testRootDirectory; 72 73 private String _path; 74 75 private Document _document; 76 77 private MockContext _context; 78 79 private String _servletName; 80 81 private ApplicationServlet _servlet; 82 83 private MockRequest _request; 84 85 private MockResponse _response; 86 87 private int _requestNumber = 0; 88 89 private Map _ognlContext; 90 91 private Throwable _exception; 92 93 96 97 private static Map _patternCache = new HashMap (); 98 99 private PatternMatcher _matcher; 100 101 private PatternCompiler _compiler; 102 103 106 107 public MockTester(String testRootDirectory, String path) throws JDOMException, 108 ServletException , DocumentParseException, IOException 109 { 110 _testRootDirectory = testRootDirectory; 111 _path = path; 112 113 parse(); 114 115 setup(); 116 } 117 118 public String toString() 119 { 120 StringBuffer buffer = new StringBuffer ("MockTester["); 121 122 if (_document != null) 123 buffer.append(_document); 124 125 buffer.append(']'); 126 127 return buffer.toString(); 128 } 129 130 133 134 public void execute() throws IOException , ServletException , DocumentParseException 135 { 136 Element root = _document.getRootElement(); 137 138 List l = root.getChildren("request"); 139 int count = l.size(); 140 141 for (int i = 0; i < count; i++) 142 { 143 Element request = (Element) l.get(i); 144 145 _requestNumber = i + 1; 146 147 executeRequest(request); 148 } 149 150 _servlet.destroy(); 151 } 152 153 private void executeRequest(Element request) throws IOException , ServletException , 154 DocumentParseException 155 { 156 Cookie [] oldRequestCookies = (_request == null ? null : _request.getCookies()); 157 158 _request = new MockRequest(_context, "/" + _servlet.getServletName()); 159 160 String contentType = request.getAttributeValue("content-type"); 161 if (contentType != null) 162 _request.setContentType(contentType); 163 164 String contentPath = request.getAttributeValue("content-path"); 165 if (contentPath != null) 166 _request.setContentPath(_testRootDirectory + contentPath); 167 168 _request.addCookies(oldRequestCookies); 169 170 if (_response != null) 171 _request.addCookies(_response.getCookies()); 172 173 setupRequest(request); 174 175 _exception = null; 176 177 _response = new MockResponse(_request); 178 179 try 180 { 181 _servlet.service(_request, _response); 182 } 183 catch (ServletException ex) 184 { 185 _exception = ex; 186 } 187 catch (IOException ex) 188 { 189 _exception = ex; 190 } 191 192 _response.end(); 193 194 System.out.println("=== Response #" + _requestNumber + " ===\n\n"); 195 System.out.println(_response.getOutputString()); 196 System.out.println("\n\n"); 197 198 executeAssertions(request); 199 } 200 201 private void parse() throws JDOMException, DocumentParseException, IOException 202 { 203 SAXBuilder builder = new SAXBuilder(); 204 205 _document = builder.build(_path); 206 } 207 208 private void setup() throws ServletException 209 { 210 Element root = _document.getRootElement(); 211 212 if (!root.getName().equals("mock-test")) 213 throw new RuntimeException ("Root element of " + _path + " must be 'mock-test'."); 214 215 setupContext(root); 216 setupServlet(root); 217 } 218 219 private void setupContext(Element parent) 220 { 221 _context = new MockContext(_testRootDirectory); 222 223 Element context = parent.getChild("context"); 224 225 if (context == null) 226 return; 227 228 String name = context.getAttributeValue("name"); 229 230 if (name != null) 231 _context.setServletContextName(name); 232 233 String root = context.getAttributeValue("root"); 234 235 if (root != null) 236 _context.setRootDirectory(_testRootDirectory + root); 237 238 setInitParameters(context, _context); 239 } 240 241 private void setupServlet(Element parent) throws ServletException 242 { 243 Element servlet = parent.getChild("servlet"); 244 245 String className = servlet.getAttributeValue("class"); 246 _servletName = servlet.getAttributeValue("name"); 247 248 _servlet = createServlet(className); 249 250 MockServletConfig config = new MockServletConfig(_servletName, _context); 251 252 setInitParameters(servlet, config); 253 254 _servlet.init(config); 255 } 256 257 private void setupRequest(Element request) 258 { 259 String method = request.getAttributeValue("method"); 260 if (method != null) 261 _request.setMethod(method); 262 263 265 String locale = request.getAttributeValue("locale"); 266 if (locale != null) 267 _request.setLocale(new Locale (locale, "", "")); 268 269 List parameters = request.getChildren("parameter"); 270 int count = parameters.size(); 271 272 for (int i = 0; i < count; i++) 273 { 274 Element parameter = (Element) parameters.get(i); 275 276 setRequestParameter(parameter); 277 } 278 279 String failover = request.getAttributeValue("failover"); 280 281 if (failover != null && failover.equals("true")) 282 _request.simulateFailover(); 283 284 } 286 287 private void setRequestParameter(Element parameter) 288 { 289 List values = new ArrayList (); 290 291 String name = parameter.getAttributeValue("name"); 292 293 String value = parameter.getAttributeValue("value"); 294 if (value != null) 295 values.add(value); 296 297 List children = parameter.getChildren("value"); 298 int count = children.size(); 299 for (int i = 0; i < count; i++) 300 { 301 Element e = (Element) children.get(i); 302 value = e.getTextTrim(); 303 304 values.add(value); 305 } 306 307 String [] array = (String []) values.toArray(new String [values.size()]); 308 309 _request.setParameter(name, array); 310 } 311 312 private void setInitParameters(Element parent, InitParameterHolder holder) 313 { 314 List children = parent.getChildren("init-parameter"); 315 316 int count = children.size(); 317 for (int i = 0; i < count; i++) 318 { 319 Element e = (Element) children.get(i); 320 321 String name = e.getAttributeValue("name"); 322 String value = e.getAttributeValue("value"); 323 324 holder.setInitParameter(name, value); 325 } 326 } 327 328 private ApplicationServlet createServlet(String className) 329 { 330 Throwable t = null; 331 try 332 { 333 Class servletClass = Class.forName(className); 334 335 return (ApplicationServlet) servletClass.newInstance(); 336 } 337 catch (ClassNotFoundException ex) 338 { 339 t = ex; 340 } 341 catch (InstantiationException ex) 342 { 343 t = ex; 344 } 345 catch (IllegalAccessException ex) 346 { 347 t = ex; 348 } 349 350 353 throw new ApplicationRuntimeException("Unable to instantiate servlet class " + className 354 + ".", t); 355 } 356 357 public MockContext getContext() 358 { 359 return _context; 360 } 361 362 public MockRequest getRequest() 363 { 364 return _request; 365 } 366 367 public MockResponse getResponse() 368 { 369 return _response; 370 } 371 372 public ApplicationServlet getServlet() 373 { 374 return _servlet; 375 } 376 377 private void executeAssertions(Element request) throws DocumentParseException 378 { 379 executeOutputAssertions(request); 380 executeNoOutputAssertions(request); 381 executeRegexpAssertions(request); 382 executeExpressionAssertions(request); 383 executeOutputMatchesAssertions(request); 384 executeCookieAssertions(request); 385 executeOutputStreamAssertions(request); 386 executeExceptionAssertions(request); 387 } 388 389 393 394 private void executeExpressionAssertions(Element request) throws DocumentParseException 395 { 396 List l = request.getChildren("assert"); 397 int count = l.size(); 398 399 for (int i = 0; i < count; i++) 400 { 401 Element a = (Element) l.get(i); 402 403 String name = a.getAttributeValue("name"); 404 String expression = a.getTextTrim(); 405 406 checkExpression(name, expression); 407 } 408 } 409 410 private void checkExpression(String name, String expression) throws DocumentParseException 411 { 412 413 boolean result = evaluate(expression); 414 415 if (result) 416 return; 417 418 throw new AssertionFailedError(buildTestName(name) + ": Expression '" + expression 419 + "' was not true."); 420 421 } 422 423 private boolean evaluate(String expression) throws DocumentParseException 424 { 425 if (_ognlContext == null) 426 _ognlContext = Ognl.createDefaultContext(this); 427 428 Object value = null; 429 430 try 431 { 432 value = Ognl.getValue(expression, _ognlContext, this); 433 } 434 catch (OgnlException ex) 435 { 436 throw new DocumentParseException("Expression '" + expression + "' is not valid.", ex); 437 } 438 439 if (value == null) 440 return false; 441 442 if (value instanceof Boolean ) 443 return ((Boolean ) value).booleanValue(); 444 445 if (value instanceof Number ) 446 return ((Number ) value).longValue() != 0; 447 448 if (value instanceof String ) 449 return ((String ) value).length() > 0; 450 451 throw new DocumentParseException("Expression '" + expression + "' evaluates to (" 452 + value.getClass().getName() + ") " + value 453 + ", which cannot be interpreted as a boolean.", null); 454 } 455 456 462 463 private void executeRegexpAssertions(Element request) throws DocumentParseException 464 { 465 String outputString = null; 466 467 List assertions = request.getChildren("assert-regexp"); 468 int count = assertions.size(); 469 470 for (int i = 0; i < count; i++) 471 { 472 Element a = (Element) assertions.get(i); 473 474 String name = a.getAttributeValue("name"); 475 String pattern = a.getTextTrim(); 476 477 if (HiveMind.isBlank(pattern)) 478 throw new DocumentParseException("Pattern is null in " + a, null); 479 480 if (outputString == null) 481 outputString = _response.getOutputString(); 482 483 matchRegexp(name, outputString, pattern); 484 } 485 486 } 487 488 494 495 private void executeOutputAssertions(Element request) throws DocumentParseException 496 { 497 String outputString = null; 498 499 List assertions = request.getChildren("assert-output"); 500 int count = assertions.size(); 501 502 for (int i = 0; i < count; i++) 503 { 504 Element a = (Element) assertions.get(i); 505 506 String name = a.getAttributeValue("name"); 507 String substring = a.getTextTrim(); 508 509 if (HiveMind.isBlank(substring)) 510 throw new DocumentParseException("Substring is null in " + a, null); 511 512 if (outputString == null) 513 outputString = _response.getOutputString(); 514 515 matchSubstring(name, outputString, substring); 516 } 517 518 } 519 520 526 527 private void executeNoOutputAssertions(Element request) throws DocumentParseException 528 { 529 String outputString = null; 530 531 List assertions = request.getChildren("assert-no-output"); 532 int count = assertions.size(); 533 534 for (int i = 0; i < count; i++) 535 { 536 Element a = (Element) assertions.get(i); 537 538 String name = a.getAttributeValue("name"); 539 String substring = a.getTextTrim(); 540 541 if (HiveMind.isBlank(substring)) 542 throw new DocumentParseException("Substring is null in " + a, null); 543 544 if (outputString == null) 545 outputString = _response.getOutputString(); 546 547 matchNoSubstring(name, outputString, substring); 548 } 549 550 } 551 552 private PatternMatcher getMatcher() 553 { 554 if (_matcher == null) 555 _matcher = new Perl5Matcher(); 556 557 return _matcher; 558 } 559 560 private Pattern compile(String pattern) throws DocumentParseException 561 { 562 Pattern result = (Pattern) _patternCache.get(pattern); 563 564 if (result != null) 565 return result; 566 567 if (_compiler == null) 568 _compiler = new Perl5Compiler(); 569 570 try 571 { 572 result = _compiler.compile(pattern, Perl5Compiler.MULTILINE_MASK); 573 574 } 575 catch (MalformedPatternException ex) 576 { 577 throw new ApplicationRuntimeException("Malformed regular expression: " + pattern 578 + " in " + _path + ".", ex); 579 } 580 581 _patternCache.put(pattern, result); 582 583 return result; 584 } 585 586 private void matchRegexp(String name, String text, String pattern) 587 throws DocumentParseException 588 { 589 Pattern compiled = compile(pattern); 590 591 if (getMatcher().contains(text, compiled)) 592 return; 593 594 System.err.println(text); 595 596 throw new AssertionFailedError(buildTestName(name) 597 + ": Response does not contain regular expression '" + pattern + "'."); 598 } 599 600 private void matchSubstring(String name, String text, String substring) 601 { 602 if (text == null) 603 throw new AssertionFailedError(buildTestName(name) + " : Response is null."); 604 605 if (text.indexOf(substring) >= 0) 606 return; 607 608 System.err.println(text); 609 610 throw new AssertionFailedError(buildTestName(name) + ": Response does not contain string '" 611 + substring + "'."); 612 } 613 614 private void matchNoSubstring(String name, String text, String substring) 615 { 616 if (text == null) 617 throw new AssertionFailedError(buildTestName(name) + " : Response is null."); 618 619 if (text.indexOf(substring) < 0) 620 return; 621 622 System.err.println(text); 623 624 throw new AssertionFailedError(buildTestName(name) + ": Response contains string '" 625 + substring + "'."); 626 } 627 628 private void executeOutputMatchesAssertions(Element request) throws DocumentParseException 629 { 630 List l = request.getChildren("assert-output-matches"); 631 int count = l.size(); 632 String outputString = null; 633 634 for (int i = 0; i < count; i++) 635 { 636 Element e = (Element) l.get(i); 637 638 if (outputString == null) 639 outputString = _response.getOutputString(); 640 641 executeOutputMatchAssertion(e, outputString); 642 } 643 644 } 645 646 private void executeOutputMatchAssertion(Element element, String outputString) 647 throws DocumentParseException 648 { 649 String name = element.getAttributeValue("name"); 650 String value = element.getAttributeValue("subgroup"); 651 int subgroup = (value == null) ? 0 : Integer.parseInt(value); 652 653 String pattern = element.getTextTrim(); 654 655 if (HiveMind.isBlank(pattern)) 656 throw new DocumentParseException("Pattern is null in " + element, null); 657 658 PatternMatcherInput input = new PatternMatcherInput(outputString); 659 660 PatternMatcher matcher = getMatcher(); 661 Pattern compiled = compile(pattern); 662 663 List l = element.getChildren("match"); 664 int count = l.size(); 665 int i = 0; 666 667 while (matcher.contains(input, compiled)) 668 { 669 MatchResult match = matcher.getMatch(); 670 671 if (i >= count) 672 { 673 System.err.println(outputString); 674 throw new AssertionFailedError(buildTestName(name) + ": Too many matches for '" 675 + pattern + "'."); 676 } 677 678 Element e = (Element) l.get(i); 679 String expected = e.getTextTrim(); 680 String actual = match.group(subgroup); 681 682 if (!actual.equals(expected)) 683 { 684 System.err.println(outputString); 685 throw new AssertionFailedError(buildTestName(name) + "[" + i + "]: Expected '" 686 + expected + "' but got '" + actual + "'."); 687 } 688 689 i++; 690 } 691 692 if (i < count) 693 { 694 System.err.println(outputString); 695 throw new AssertionFailedError(buildTestName(name) + ": Too few matches for '" 696 + pattern + "' (expected " + count + " but got " + i + ")."); 697 } 698 } 699 700 private void executeExceptionAssertions(Element request) 701 { 702 List l = request.getChildren("assert-exception"); 703 int count = l.size(); 704 705 for (int i = 0; i < count; i++) 706 { 707 Element assertion = (Element) l.get(i); 708 709 executeExceptionAssertion(assertion); 710 } 711 712 } 713 714 private void executeExceptionAssertion(Element assertion) 715 { 716 String name = assertion.getAttributeValue("name"); 717 String value = assertion.getTextTrim(); 718 719 if (_exception == null) 720 throw new AssertionFailedError(buildTestName(name) + " no exception thrown."); 721 String message = _exception.getMessage(); 722 723 if (message.indexOf(value) >= 0) 724 return; 725 726 throw new AssertionFailedError(buildTestName(name) + " exception message (" + message 727 + ") does not contain '" + value + "'."); 728 } 729 730 private void executeCookieAssertions(Element request) 731 { 732 List l = request.getChildren("assert-cookie"); 733 int count = l.size(); 734 735 for (int i = 0; i < count; i++) 736 { 737 Element assertion = (Element) l.get(i); 738 739 executeCookieAssertion(assertion); 740 } 741 } 742 743 private void executeCookieAssertion(Element assertion) 744 { 745 String name = assertion.getAttributeValue("name"); 746 String value = assertion.getAttributeValue("value"); 747 748 Cookie [] cookies = _response.getCookies(); 749 750 for (int i = 0; i < cookies.length; i++) 751 { 752 if (!cookies[i].getName().equals(name)) 753 continue; 754 755 if (cookies[i].getValue().equals(value)) 756 return; 757 758 throw new AssertionFailedError(buildTestName(name) + ": Response cookie '" + name 759 + "': expected '" + value + "', but was '" + cookies[i].getValue() + "'."); 760 } 761 762 throw new AssertionFailedError(buildTestName(name) + ": Could not find cookie named '" 763 + name + "' in response."); 764 } 765 766 private String buildTestName(String name) 767 { 768 return "Request #" + _requestNumber + "/" + name; 769 } 770 771 private void executeOutputStreamAssertions(Element request) throws DocumentParseException 772 { 773 List l = request.getChildren("assert-output-stream"); 774 int count = l.size(); 775 776 for (int i = 0; i < count; i++) 777 { 778 Element assertion = (Element) l.get(i); 779 780 executeOutputStreamAssertion(assertion); 781 } 782 783 } 784 785 private void executeOutputStreamAssertion(Element element) throws DocumentParseException 786 { 787 String name = element.getAttributeValue("name"); 788 String contentType = element.getAttributeValue("content-type"); 789 String path = element.getAttributeValue("path"); 790 791 String actualContentType = _response.getContentType(); 792 793 if (!contentType.equals(actualContentType)) 794 throw new AssertionFailedError(buildTestName(name) + " content-type was '" 795 + actualContentType + "', expected '" + contentType + "'."); 796 797 byte[] actualContent = _response.getResponseBytes(); 798 byte[] expectedContent = getFileContent(TestMocks.getBaseDirectory() + "/" + path); 799 800 if (actualContent.length != expectedContent.length) 801 throw new AssertionFailedError(buildTestName(name) + " actual length of " 802 + actualContent.length + " bytes does not match expected length of " 803 + expectedContent.length + " bytes."); 804 805 for (int i = 0; i < actualContent.length; i++) 806 { 807 if (actualContent[i] != expectedContent[i]) 808 throw new AssertionFailedError(buildTestName(name) 809 + " content mismatch at index + " + i + "."); 810 811 } 812 } 813 814 private byte[] getFileContent(String path) 815 { 816 try 817 { 818 InputStream in = new FileInputStream (path); 819 ByteArrayOutputStream out = new ByteArrayOutputStream (); 820 821 byte[] buffer = new byte[1000]; 822 823 while (true) 824 { 825 int length = in.read(buffer); 826 if (length < 0) 827 break; 828 829 out.write(buffer, 0, length); 830 } 831 832 in.close(); 833 out.close(); 834 835 return out.toByteArray(); 836 } 837 catch (FileNotFoundException ex) 838 { 839 throw new ApplicationRuntimeException("File '" + path + "' not found.", ex); 840 } 841 catch (IOException ex) 842 { 843 throw new ApplicationRuntimeException("Unable to read file '" + path + "'.", ex); 844 } 845 } 846 847 } | Popular Tags |