| OLD | NEW | 
|    1 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file |    1 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file | 
|    2 // for details. All rights reserved. Use of this source code is governed by a |    2 // for details. All rights reserved. Use of this source code is governed by a | 
|    3 // BSD-style license that can be found in the LICENSE file. |    3 // BSD-style license that can be found in the LICENSE file. | 
|    4  |    4  | 
|    5 /** A pipeline task for running DumpRenderTree. */ |    5 /** A pipeline task for running DumpRenderTree. */ | 
|    6 class DrtTask extends RunProcessTask { |    6 class DrtTask extends RunProcessTask { | 
|    7  |    7  | 
|    8   DrtTask(String htmlFileTemplate) { |    8   String _testFileTemplate; | 
 |    9  | 
 |   10   DrtTask(this._testFileTemplate, String htmlFileTemplate) { | 
|    9     init(config.drtPath, ['--no-timeout', htmlFileTemplate], config.timeout); |   11     init(config.drtPath, ['--no-timeout', htmlFileTemplate], config.timeout); | 
|   10   } |   12   } | 
 |   13  | 
 |   14   // In order to extract the relevant parts of the DRT render text | 
 |   15   // output we use a somewhat kludgy approach, but it should be robust. | 
 |   16   // DRT formats output with indentation, and we know that the test title | 
 |   17   // is on a line with 12 spaces indent followed by 'text run', while the | 
 |   18   // IFrame body elements are all indented at least 18 spaces. | 
 |   19   const TEST_LABEL_LINE_PREFIX = '            text run'; | 
 |   20   const BODY_LINE_PREFIX = '                  '; | 
 |   21  | 
 |   22   void execute(Path testfile, List stdout, List stderr, bool logging, | 
 |   23                Function exitHandler) { | 
 |   24  | 
 |   25     var testname = expandMacros(_testFileTemplate, testfile); | 
 |   26     var isLayout = isLayoutRenderTest(testname) || config.generateRenders; | 
 |   27  | 
 |   28     if (!isLayout) { | 
 |   29       super.execute(testfile, stdout, stderr, logging, exitHandler); | 
 |   30     } else { | 
 |   31       var tmpLog = new List<String>(); | 
 |   32       super.execute(testfile, tmpLog, tmpLog, true, | 
 |   33           (code) { | 
 |   34             var layoutFile = layoutFileName(testname); | 
 |   35             var layouts = getFileContents(layoutFile, false); | 
 |   36             var i = 0; | 
 |   37             StringBuffer sbuf = null; | 
 |   38             if (config.generateRenders) { | 
 |   39               sbuf = new StringBuffer(); | 
 |   40             } | 
 |   41             while ( i < tmpLog.length) { | 
 |   42               var line = tmpLog[i]; | 
 |   43               if (logging) { | 
 |   44                 stdout.add(line); | 
 |   45               } | 
 |   46               if (line.startsWith(TEST_LABEL_LINE_PREFIX)) { | 
 |   47                 var j = i+1; | 
 |   48                 var start = -1, end = -1; | 
 |   49                 // Walk forward to the next label or end of log. | 
 |   50                 while (j < tmpLog.length && | 
 |   51                     !tmpLog[j].startsWith(TEST_LABEL_LINE_PREFIX)) { | 
 |   52                   // Is this a body render line? | 
 |   53                   if (tmpLog[j].startsWith(BODY_LINE_PREFIX)) { | 
 |   54                     if (start < 0) { // Is it the first? | 
 |   55                       start = j; | 
 |   56                     } | 
 |   57                   } else { // Not a render line. | 
 |   58                     if (start >= 0 && end < 0) { | 
 |   59                       // We were just in a set of render lines, so this | 
 |   60                       // line is the first non-member. | 
 |   61                       end = j; | 
 |   62                     } | 
 |   63                   } | 
 |   64                   j++; | 
 |   65                 } | 
 |   66                 if (start >= 0) { // We have some render lines. | 
 |   67                   if (end < 0) { | 
 |   68                     end = tmpLog.length; // Sanity. | 
 |   69                   } | 
 |   70                   var actualLayout = new List<String>(); | 
 |   71                   while (start < end) { | 
 |   72                     actualLayout.add( | 
 |   73                         tmpLog[start++].substring(BODY_LINE_PREFIX.length)); | 
 |   74                   } | 
 |   75                   var testName = checkTest(testfile, line, layouts, | 
 |   76                                             actualLayout, stdout); | 
 |   77                   if (testName == null) { | 
 |   78                     code = -1; | 
 |   79                   } else if (config.generateRenders) { | 
 |   80                     sbuf.add(testName); | 
 |   81                     sbuf.add('\n'); | 
 |   82                     for (var renderLine in actualLayout) { | 
 |   83                       sbuf.add(renderLine); | 
 |   84                       sbuf.add('\n'); | 
 |   85                     } | 
 |   86                   } | 
 |   87                 } | 
 |   88                 i = j; | 
 |   89               } else { | 
 |   90                 i++; | 
 |   91               } | 
 |   92             } | 
 |   93             if (config.generateRenders) { | 
 |   94               createFile(layoutFile, sbuf.toString()); | 
 |   95             } | 
 |   96             exitHandler(code); | 
 |   97           }); | 
 |   98     } | 
 |   99   } | 
 |  100  | 
 |  101   /** | 
 |  102    * Verify whether a test passed - it must pass the code expectations, | 
 |  103    * and have validated layout. Report success or failure in a test | 
 |  104    * result message. Upon success the render section name is returned | 
 |  105    * (useful for `config.generateRenders`); otherwise null is returned. | 
 |  106    */ | 
 |  107   String checkTest(Path testfile, String label, List layouts, | 
 |  108                  List actual, List out) { | 
 |  109     var testGroup = null; | 
 |  110     var testName = null; | 
 |  111  | 
 |  112     // The label line has the form: | 
 |  113     // "result:duration:<test>//message" | 
 |  114     // where <test> is a test name or series of one or more group names | 
 |  115     // followed by a test name, separated by ###. | 
 |  116  | 
 |  117     // First extract test state, duration, name and message. If the test | 
 |  118     // passed we can ignore these and continue to layout verification, but | 
 |  119     // if the test failed we want to know that so we can report failure. | 
 |  120     // | 
 |  121     // TODO(gram) - currently we lose the stack trace although the user | 
 |  122     // will get it in the overall output if they used --verbose. We may | 
 |  123     // want to fix this properly at some point. | 
 |  124     var labelParser = const RegExp('\"\([a-z]*\):\([0-9]*\):\(.*\)//\(.*\)\"'); | 
 |  125     Match match = labelParser.firstMatch(label); | 
 |  126     var result = match.group(1); | 
 |  127     var duration = parseDouble(match.group(2)) / 1000; | 
 |  128     var test = match.group(3); | 
 |  129     var message = match.group(4); | 
 |  130  | 
 |  131     // Split name up with group. | 
 |  132     var idx = test.lastIndexOf('###'); | 
 |  133     if (idx >= 0) { | 
 |  134       testGroup = test.substring(0, idx).replaceAll('###', ' '); | 
 |  135       testName = test.substring(idx+3); | 
 |  136     } else { | 
 |  137       testGroup = ''; | 
 |  138       testName = test; | 
 |  139     } | 
 |  140     var section = '[${_pad(testGroup)}$testName]'; | 
 |  141  | 
 |  142     if (config.generateRenders) { | 
 |  143       // Do nothing; fake a pass. | 
 |  144       out.add(_formatMessage(config.passFormat, | 
 |  145                              testfile, testGroup, testName, duration, '')); | 
 |  146     } else if (result != 'pass') { | 
 |  147       // The test failed separately from layout; just report that | 
 |  148       // failure. | 
 |  149       out.add(_formatMessage( | 
 |  150           (result == 'fail' ? config.failFormat : config.errorFormat), | 
 |  151               testfile, testGroup, testName, duration, message)); | 
 |  152       return null; | 
 |  153     } else { | 
 |  154       // The test passed, at least the expectations. So check the layout. | 
 |  155       var expected = _getLayout(layouts, section); | 
 |  156       var failMessage = null; | 
 |  157       var lineNum = 0; | 
 |  158       if (expected != null) { | 
 |  159         while (lineNum < expected.length) { | 
 |  160           if (lineNum >= actual.length) { | 
 |  161             failMessage = 'expected "${expected[lineNum]}" but got nothing'; | 
 |  162             break; | 
 |  163           } else { | 
 |  164             if (expected[lineNum] != actual[lineNum]) { | 
 |  165               failMessage = 'expected "${expected[lineNum]}" ' | 
 |  166                             'but got "${actual[lineNum]}"'; | 
 |  167               break; | 
 |  168             } | 
 |  169           } | 
 |  170           lineNum++; | 
 |  171         } | 
 |  172         if (failMessage == null && lineNum < actual.length) { | 
 |  173           failMessage = 'expected nothing but got "${actual[lineNum]}"'; | 
 |  174         } | 
 |  175       } | 
 |  176       if (failMessage != null) { | 
 |  177         out.add(_formatMessage(config.failFormat, | 
 |  178                                testfile, testGroup, testName, duration, | 
 |  179                                'Layout content mismatch at line $lineNum: ' | 
 |  180                                '$failMessage')); | 
 |  181         return null; | 
 |  182       } else { | 
 |  183         out.add(_formatMessage(config.passFormat, | 
 |  184                                testfile, testGroup, testName, duration, '')); | 
 |  185       } | 
 |  186     } | 
 |  187     return section; | 
 |  188   } | 
 |  189  | 
 |  190   /** Get the expected layout for a test. */ | 
 |  191   List _getLayout(List layouts, String section) { | 
 |  192     List layout = new List(); | 
 |  193     for (var i = 0; i < layouts.length; i++) { | 
 |  194       if (layouts[i] == section) { | 
 |  195         ++i; | 
 |  196         while (i < layouts.length && !layouts[i].startsWith('[')) { | 
 |  197           layout.add(layouts[i++]); | 
 |  198         } | 
 |  199         break; | 
 |  200       } | 
 |  201     } | 
 |  202     return layout; | 
 |  203   } | 
 |  204  | 
 |  205   /** Pad a string with a rightmost space unless it is empty. */ | 
 |  206   static String _pad(s) => (s.length > 0) ? '$s ' : s; | 
 |  207  | 
 |  208   /** Format a test result message. */ | 
 |  209   String _formatMessage(String format, | 
 |  210                         Path testfile, String testGroup, String testName, | 
 |  211                         double duration, String message) { | 
 |  212     String fname = makePathAbsolute(testfile.directoryPath.toString()); | 
 |  213     return "###${format. | 
 |  214       replaceAll(Macros.testTime, '${duration.toStringAsFixed(3)}s '). | 
 |  215       replaceAll(Macros.testfile, _pad(fname)). | 
 |  216       replaceAll(Macros.testGroup, _pad(testGroup)). | 
 |  217       replaceAll(Macros.testDescription, _pad(testName)). | 
 |  218       replaceAll(Macros.testMessage, _pad(message))}"; | 
 |  219   } | 
|   11 } |  220 } | 
| OLD | NEW |