| Index: dart/tests/try/web/program_result.dart | 
| diff --git a/dart/tests/try/web/program_result.dart b/dart/tests/try/web/program_result.dart | 
| index 2bd31007259a17035a467f5214fd7190fcc6712d..1ecc05a7f6177517ba129e57ce25bb5a0ccb6694 100644 | 
| --- a/dart/tests/try/web/program_result.dart | 
| +++ b/dart/tests/try/web/program_result.dart | 
| @@ -4,10 +4,13 @@ | 
|  | 
| library trydart.test.program_result; | 
|  | 
| +import 'dart:convert' show | 
| +    JSON; | 
| + | 
| import '../poi/source_update.dart'; | 
|  | 
| class ProgramResult { | 
| -  final String code; | 
| +  final /* Map<String, String> or String */ code; | 
|  | 
| final List<String> messages; | 
|  | 
| @@ -19,6 +22,14 @@ class ProgramResult { | 
| List<String> messagesWith(String extra) { | 
| return new List<String>.from(messages)..add(extra); | 
| } | 
| + | 
| +  String toString() { | 
| +    return """ | 
| +ProgramResult( | 
| +    ${JSON.encode(code)}, | 
| +    ${JSON.encode(messages)}, | 
| +    compileUpdatesShouldThrow: $compileUpdatesShouldThrow)"""; | 
| +  } | 
| } | 
|  | 
| class ProgramExpectation { | 
| @@ -36,26 +47,100 @@ class ProgramExpectation { | 
| } | 
|  | 
| class EncodedResult { | 
| -  final List updates; | 
| +  final /* String or List */ updates; | 
|  | 
| -  final List<ProgramExpectation> expectations; | 
| +  final List expectations; | 
|  | 
| const EncodedResult(this.updates, this.expectations); | 
|  | 
| List<ProgramResult> decode() { | 
| -    if (updates.length == 1) { | 
| -      throw new StateError("Trivial diff, no reason to use decode."); | 
| -    } | 
| -    List<String> sources = expandUpdates(updates); | 
| -    if (sources.length != expectations.length) { | 
| -      throw new StateError( | 
| -          "Number of sources and expectations differ " | 
| -          "(${sources.length} sources, ${expectations.length} expectations)."); | 
| +    if (updates is List) { | 
| +      if (updates.length == 1) { | 
| +        throw new StateError("Trivial diff, no reason to use decode."); | 
| +      } | 
| +      List<String> sources = expandUpdates(updates); | 
| +      if (sources.length != expectations.length) { | 
| +        throw new StateError( | 
| +            "Number of sources and expectations differ" | 
| +            " (${sources.length} sources," | 
| +            " ${expectations.length} expectations)."); | 
| +      } | 
| +      List<ProgramResult> result = new List<ProgramResult>(sources.length); | 
| +      for (int i = 0; i < sources.length; i++) { | 
| +        result[i] = expectations[i].toResult(sources[i]); | 
| +      } | 
| +      return result; | 
| +    } else if (updates is String) { | 
| +      Map<String, String> files = splitFiles(updates); | 
| +      Map<String, List<String>> fileMap = <String, List<String>>{}; | 
| +      int updateCount = -1; | 
| +      for (String name in files.keys) { | 
| +        if (name.endsWith(".patch")) { | 
| +          String realname = name.substring(0, name.length - ".patch".length); | 
| +          if (files.containsKey(realname)) { | 
| +            throw new StateError("Patch '$name' conflicts with '$realname'"); | 
| +          } | 
| +          if (fileMap.containsKey(realname)) { | 
| +            // Can't happen. | 
| +            throw new StateError("Duplicated entry for '$realname'."); | 
| +          } | 
| +          List<String> updates = expandUpdates(expandDiff(files[name])); | 
| +          if (updates.length == 1) { | 
| +            throw new StateError("No patches found in:\n ${files[name]}"); | 
| +          } | 
| +          if (updateCount == -1) { | 
| +            updateCount = updates.length; | 
| +          } else if (updateCount != updates.length) { | 
| +            throw new StateError( | 
| +                "Unexpected number of patches: ${updates.length}," | 
| +                " expected ${updateCount}"); | 
| +          } | 
| +          fileMap[realname] = updates; | 
| +        } | 
| +      } | 
| +      if (updateCount == -1) { | 
| +        throw new StateError("No patch files in $updates"); | 
| +      } | 
| +      for (String name in files.keys) { | 
| +        if (!name.endsWith(".patch")) { | 
| +          fileMap[name] = new List<String>.filled(updateCount, files[name]); | 
| +        } | 
| +      } | 
| +      if (updateCount != expectations.length) { | 
| +        throw new StateError( | 
| +            "Number of patches and expectations differ " | 
| +            "(${updateCount} patches, ${expectations.length} expectations)."); | 
| +      } | 
| +      List<ProgramResult> result = new List<ProgramResult>(updateCount); | 
| +      int i = 0; | 
| +      for (String name in fileMap.keys) { | 
| +        ProgramExpectation expectation = decodeExpectation(expectations[i]); | 
| +        result[i] = new ProgramResult( | 
| +            <String, String>{}, | 
| +            expectation.messages, | 
| +            compileUpdatesShouldThrow: expectation.compileUpdatesShouldThrow); | 
| +        i++; | 
| +      } | 
| +      for (String name in fileMap.keys) { | 
| +        for (int j = 0; j < updateCount; j++) { | 
| +          result[j].code[name] = fileMap[name][j]; | 
| +        } | 
| +      } | 
| +      return result; | 
| +    } else { | 
| +      throw new StateError("Unknown encoding of updates"); | 
| } | 
| -    List<ProgramResult> result = new List<ProgramResult>(sources.length); | 
| -    for (int i = 0; i < sources.length; i++) { | 
| -      result[i] = expectations[i].toResult(sources[i]); | 
| -    } | 
| -    return result; | 
| +  } | 
| +} | 
| + | 
| +ProgramExpectation decodeExpectation(expectation) { | 
| +  if (expectation is ProgramExpectation) { | 
| +    return expectation; | 
| +  } else if (expectation is String) { | 
| +    return new ProgramExpectation(<String>[expectation]); | 
| +  } else if (expectation is List) { | 
| +    return new ProgramExpectation(new List<String>.from(expectation)); | 
| +  } else { | 
| +    throw new ArgumentError("Don't know how to decode $expectation"); | 
| } | 
| } | 
|  |