/** * Copyright 2007-2015, Kaazing Corporation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kaazing.k3po.driver.internal.behavior; import static java.util.Objects.requireNonNull; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.kaazing.k3po.lang.internal.RegionInfo; public class ScriptProgress { private final String expectedScript; private final RegionInfo scriptInfo; private final Map<RegionInfo, String> failureInfos; private String observeredScript; public ScriptProgress(RegionInfo scriptInfo, String expectedScript) { this.expectedScript = expectedScript; this.scriptInfo = requireNonNull(scriptInfo); this.failureInfos = new ConcurrentHashMap<>(); } public void addScriptFailure(RegionInfo regionInfo) { failureInfos.put(regionInfo, ""); } public void addScriptFailure(RegionInfo regionInfo, String message) { failureInfos.put(regionInfo, message); } public String getExpectedScript() { return expectedScript; } public RegionInfo getScriptInfo() { return scriptInfo; } public String getObservedScript() { if (observeredScript == null) { int numberOfFailures = failureInfos.size(); if (numberOfFailures == 0) { // no failures observeredScript = expectedScript; } else { StringBuilder builder = new StringBuilder(); processRegion(builder, scriptInfo, failureInfos); // Failures to unexpected events (e.g. channel close) are artificially added // potentially resulting in multiple failures on the same line with only one // being reported if (numberOfFailures <= failureInfos.size()) { throw new RuntimeException("Script failure detected but not located"); } observeredScript = builder.toString(); } } return observeredScript; } private boolean processRegion(StringBuilder builder, RegionInfo regionInfo, Map<RegionInfo, String> failureInfos) { String failure = failureInfos.remove(regionInfo); if (failure != null) { builder.append(failure); if (regionInfo.kind == RegionInfo.Kind.PARALLEL) { return true; } return false; } List<RegionInfo> childInfos = regionInfo.children; int previousEnd = regionInfo.start; for (Iterator<RegionInfo> $i = childInfos.iterator(); $i.hasNext();) { RegionInfo childInfo = $i.next(); builder.append(expectedScript.substring(previousEnd, childInfo.start)); previousEnd = childInfo.end; boolean status = processRegion(builder, childInfo, failureInfos); if (!status) { if (regionInfo.kind == RegionInfo.Kind.PARALLEL) { while ($i.hasNext()) { childInfo = $i.next(); previousEnd = childInfo.end; } break; } return false; } } builder.append(expectedScript.substring(previousEnd, regionInfo.end)); return true; } }