| 1 | package com.github.dakusui.actionunit.visitors; | |
| 2 | ||
| 3 | import com.github.dakusui.actionunit.actions.Composite; | |
| 4 | import com.github.dakusui.actionunit.core.Action; | |
| 5 | import com.github.dakusui.actionunit.io.Writer; | |
| 6 | ||
| 7 | import java.util.LinkedList; | |
| 8 | import java.util.List; | |
| 9 | import java.util.Map; | |
| 10 | import java.util.function.Predicate; | |
| 11 | ||
| 12 | import static java.lang.String.format; | |
| 13 | import static java.util.Objects.requireNonNull; | |
| 14 | ||
| 15 | /** | |
| 16 | * A basic action reporter which writes an action tree and its report to given writers | |
| 17 | * | |
| 18 | * @see ReportingActionPerformer | |
| 19 | */ | |
| 20 | public class ActionReporter extends ActionPrinter { | |
| 21 |
2
1. lambda$static$0 : replaced boolean return with false for com/github/dakusui/actionunit/visitors/ActionReporter::lambda$static$0 → KILLED 2. lambda$static$0 : replaced boolean return with true for com/github/dakusui/actionunit/visitors/ActionReporter::lambda$static$0 → KILLED |
public static final Predicate<Action> DEFAULT_CONDITION_TO_SQUASH_ACTION = v -> v instanceof Composite; |
| 22 | private final List<Boolean> failingContext = new LinkedList<>(); | |
| 23 | private final Predicate<Action> conditionToSquashAction; | |
| 24 | private int emptyLevel = 0; | |
| 25 | private int depth = 0; | |
| 26 | private final Map<Action, Record> report; | |
| 27 | private final Writer warnWriter; | |
| 28 | private final Writer traceWriter; | |
| 29 | private final Writer debugWriter; | |
| 30 | private final Writer infoWriter; | |
| 31 | private final int forcePrintLevelForUnexercisedActions; | |
| 32 | String previousIndent = ""; | |
| 33 | ||
| 34 | /** | |
| 35 | * Creates an object of this class. | |
| 36 | * This reporter implements a functionality to squash actions to make output concise. | |
| 37 | * It is controlled by a predicate given through `conditionToSquashAction` parameter. | |
| 38 | * Also, depending on whether an action is exercised or not, a writer used to print it out will be chosen by internal logic of this class. | |
| 39 | * Furthermore, actions in a deep level of a tree will be considered "unexercised" forcibly. | |
| 40 | * The threshold level can be controlled by the parameter `forcePrintLevelForUnexercisedActions`. | |
| 41 | * | |
| 42 | * @param conditionToSquashAction A condition to "squash" a record of an action to make report concise. | |
| 43 | * @param warnWriter A writer used for "warn" level. | |
| 44 | * @param infoWriter A writer used for "info" level. | |
| 45 | * @param debugWriter A writer used for "debug" level. | |
| 46 | * @param traceWriter A writer used for "trace" level. | |
| 47 | * @param report A map that stores an action tree's result. | |
| 48 | * @param forcePrintLevelForUnexercisedActions A level under which action should be considered to be "unexercised". | |
| 49 | */ | |
| 50 | public ActionReporter(Predicate<Action> conditionToSquashAction, Writer warnWriter, Writer infoWriter, Writer debugWriter, Writer traceWriter, Map<Action, Record> report, int forcePrintLevelForUnexercisedActions) { | |
| 51 | super(infoWriter); | |
| 52 | this.conditionToSquashAction = conditionToSquashAction; | |
| 53 | this.report = requireNonNull(report); | |
| 54 | this.warnWriter = requireNonNull(warnWriter); | |
| 55 | this.debugWriter = requireNonNull(debugWriter); | |
| 56 | this.infoWriter = infoWriter; | |
| 57 | this.traceWriter = traceWriter; | |
| 58 | this.forcePrintLevelForUnexercisedActions = forcePrintLevelForUnexercisedActions; | |
| 59 | } | |
| 60 | ||
| 61 | /** | |
| 62 | * Creates an action of this class. | |
| 63 | * | |
| 64 | * @param writer A writer through which a report is written. | |
| 65 | * @param report A report data that records the result of actions in the tree. | |
| 66 | */ | |
| 67 | public ActionReporter(Writer writer, Map<Action, Record> report) { | |
| 68 | this(DEFAULT_CONDITION_TO_SQUASH_ACTION, writer, writer, writer, writer, report, 2); | |
| 69 | } | |
| 70 | ||
| 71 | /** | |
| 72 | * An entry-point to start reporting. | |
| 73 | * | |
| 74 | * @param action An action from which reporting starts. | |
| 75 | */ | |
| 76 | public void report(Action action) { | |
| 77 |
1
1. report : removed call to com/github/dakusui/actionunit/core/Action::accept → KILLED |
requireNonNull(action).accept(this); |
| 78 | } | |
| 79 | ||
| 80 | @Override | |
| 81 | protected void handleAction(Action action) { | |
| 82 |
1
1. handleAction : negated conditional → KILLED |
if (this.conditionToSquashAction.test(action)) { |
| 83 | this.previousIndent = indent(); | |
| 84 | return; | |
| 85 | } | |
| 86 | Record runs = report.get(action); | |
| 87 |
1
1. handleAction : negated conditional → KILLED |
String message = format("%s[%s]%s", indent(), runs != null ? runs : "", action); |
| 88 | this.previousIndent = ""; | |
| 89 |
1
1. handleAction : negated conditional → SURVIVED |
if (isInFailingContext()) { |
| 90 |
1
1. handleAction : removed call to com/github/dakusui/actionunit/io/Writer::writeLine → KILLED |
this.warnWriter.writeLine(message); |
| 91 | } else { | |
| 92 |
2
1. handleAction : changed conditional boundary → SURVIVED 2. handleAction : negated conditional → SURVIVED |
if (emptyLevel < 1) { // Top level unexercised + exercised ones |
| 93 |
2
1. handleAction : changed conditional boundary → SURVIVED 2. handleAction : negated conditional → SURVIVED |
if (passingLevels() < 1) { |
| 94 |
1
1. handleAction : removed call to com/github/dakusui/actionunit/io/Writer::writeLine → KILLED |
this.infoWriter.writeLine(message); |
| 95 | } else { | |
| 96 |
1
1. handleAction : removed call to com/github/dakusui/actionunit/visitors/ActionReporter::writeLineForUnexercisedAction → KILLED |
writeLineForUnexercisedAction(message); |
| 97 | } | |
| 98 | } else { | |
| 99 |
1
1. handleAction : removed call to com/github/dakusui/actionunit/visitors/ActionReporter::writeLineForUnexercisedAction → KILLED |
writeLineForUnexercisedAction(message); |
| 100 | } | |
| 101 | } | |
| 102 | } | |
| 103 | ||
| 104 | /** | |
| 105 | * //@formatter.off | |
| 106 | * ---- | |
| 107 | * > [E:0]for each of (noname) parallely | |
| 108 | * > +-[EE:0]print1 | |
| 109 | * > | [EE:0](noname) | |
| 110 | * > +-[]print2 | |
| 111 | * > | [](noname) | |
| 112 | * > +-[]print2-1 | |
| 113 | * > | [](noname) | |
| 114 | * > +-[]print2-2 | |
| 115 | * > [](noname) | |
| 116 | * ---- | |
| 117 | * //@format:on | |
| 118 | */ | |
| 119 | @Override | |
| 120 | public String indent() { | |
| 121 | List<? extends Action> path = this.path(); | |
| 122 | StringBuilder b = new StringBuilder(); | |
| 123 |
1
1. indent : negated conditional → KILLED |
if (!path.isEmpty()) { |
| 124 |
1
1. indent : Replaced integer subtraction with addition → KILLED |
Action last = path.get(path.size() - 1); |
| 125 | for (Action each : path) { | |
| 126 |
1
1. indent : negated conditional → KILLED |
if (each instanceof Composite) { |
| 127 |
1
1. indent : negated conditional → KILLED |
if (each == last) { |
| 128 |
1
1. indent : negated conditional → KILLED |
if (((Composite) each).isParallel()) |
| 129 | b.append("*-"); | |
| 130 | else | |
| 131 | b.append("+-"); | |
| 132 | } else { | |
| 133 |
1
1. indent : negated conditional → KILLED |
if (isLastChild(nextOf(each, path), each)) |
| 134 | b.append(" "); | |
| 135 | else | |
| 136 | b.append("| "); | |
| 137 | } | |
| 138 | } else { | |
| 139 | b.append(" "); | |
| 140 | } | |
| 141 | } | |
| 142 | } | |
| 143 |
1
1. indent : replaced return value with "" for com/github/dakusui/actionunit/visitors/ActionReporter::indent → KILLED |
return mergeStrings(this.previousIndent, b.toString()); |
| 144 | } | |
| 145 | ||
| 146 | @Override | |
| 147 | protected void enter(Action action) { | |
| 148 |
1
1. enter : removed call to com/github/dakusui/actionunit/visitors/ActionPrinter::enter → KILLED |
super.enter(action); |
| 149 |
1
1. enter : Replaced integer addition with subtraction → SURVIVED |
depth++; |
| 150 | Record runs = report.get(action); | |
| 151 |
3
1. enter : negated conditional → SURVIVED 2. enter : negated conditional → KILLED 3. enter : removed call to com/github/dakusui/actionunit/visitors/ActionReporter::pushFailingContext → KILLED |
pushFailingContext(runs != null && runs.allFailing()); |
| 152 |
1
1. enter : negated conditional → SURVIVED |
if (runs == null) |
| 153 |
1
1. enter : Replaced integer addition with subtraction → SURVIVED |
emptyLevel++; |
| 154 | ||
| 155 | } | |
| 156 | ||
| 157 | @Override | |
| 158 | protected void leave(Action action) { | |
| 159 | Record runs = report.get(action); | |
| 160 |
1
1. leave : negated conditional → SURVIVED |
if (runs == null) |
| 161 |
1
1. leave : Replaced integer subtraction with addition → SURVIVED |
emptyLevel--; |
| 162 |
1
1. leave : removed call to com/github/dakusui/actionunit/visitors/ActionReporter::popFailingContext → SURVIVED |
popFailingContext(); |
| 163 |
1
1. leave : Replaced integer subtraction with addition → SURVIVED |
depth--; |
| 164 |
1
1. leave : removed call to com/github/dakusui/actionunit/visitors/ActionPrinter::leave → KILLED |
super.leave(action); |
| 165 | } | |
| 166 | ||
| 167 | boolean isInFailingContext() { | |
| 168 |
3
1. isInFailingContext : negated conditional → SURVIVED 2. isInFailingContext : replaced boolean return with true for com/github/dakusui/actionunit/visitors/ActionReporter::isInFailingContext → SURVIVED 3. isInFailingContext : negated conditional → KILLED |
return !this.failingContext.isEmpty() && this.failingContext.get(0); |
| 169 | } | |
| 170 | ||
| 171 | void pushFailingContext(boolean newContext) { | |
| 172 |
1
1. pushFailingContext : removed call to java/util/List::add → KILLED |
failingContext.add(0, newContext); |
| 173 | } | |
| 174 | ||
| 175 | void popFailingContext() { | |
| 176 | failingContext.remove(0); | |
| 177 | } | |
| 178 | ||
| 179 | private void writeLineForUnexercisedAction(String message) { | |
| 180 | // unexercised | |
| 181 |
2
1. writeLineForUnexercisedAction : changed conditional boundary → SURVIVED 2. writeLineForUnexercisedAction : negated conditional → SURVIVED |
if (depth < this.forcePrintLevelForUnexercisedActions) |
| 182 |
1
1. writeLineForUnexercisedAction : removed call to com/github/dakusui/actionunit/io/Writer::writeLine → KILLED |
this.debugWriter.writeLine(message); |
| 183 | else | |
| 184 |
1
1. writeLineForUnexercisedAction : removed call to com/github/dakusui/actionunit/io/Writer::writeLine → KILLED |
this.traceWriter.writeLine(message); |
| 185 | } | |
| 186 | ||
| 187 | private int passingLevels() { | |
| 188 | int ret = 0; | |
| 189 | for (boolean each : this.failingContext) | |
| 190 |
1
1. passingLevels : negated conditional → SURVIVED |
if (!each) |
| 191 |
1
1. passingLevels : Changed increment from 1 to -1 → SURVIVED |
ret++; |
| 192 |
1
1. passingLevels : replaced int return with 0 for com/github/dakusui/actionunit/visitors/ActionReporter::passingLevels → SURVIVED |
return ret; |
| 193 | } | |
| 194 | ||
| 195 | private static Action nextOf(Action each, List<? extends Action> path) { | |
| 196 |
2
1. nextOf : Replaced integer addition with subtraction → KILLED 2. nextOf : replaced return value with null for com/github/dakusui/actionunit/visitors/ActionReporter::nextOf → KILLED |
return path.get(path.indexOf(each) + 1); |
| 197 | } | |
| 198 | ||
| 199 | /** | |
| 200 | * Merges two string into one. | |
| 201 | * A white space in `a` or `b` will be replaced with non-white space in the other at the same position. | |
| 202 | * In case both have non-white space in the same position, the latter's (`b`) overrides the first's (`a`). | |
| 203 | * <p> | |
| 204 | * .Example input | |
| 205 | * ---- | |
| 206 | * a:"hello " | |
| 207 | * b:" O WORLD " | |
| 208 | * ---- | |
| 209 | * <p> | |
| 210 | * .Example output | |
| 211 | * ---- | |
| 212 | * "hellO WORLD " | |
| 213 | * ---- | |
| 214 | * | |
| 215 | * @param a A string to be merged. | |
| 216 | * @param b A stringto be merged | |
| 217 | * @return The merged result string. | |
| 218 | */ | |
| 219 | private static String mergeStrings(String a, String b) { | |
| 220 | StringBuilder builder = new StringBuilder(); | |
| 221 | int min = Math.min(a.length(), b.length()); | |
| 222 |
3
1. mergeStrings : changed conditional boundary → KILLED 2. mergeStrings : Changed increment from 1 to -1 → KILLED 3. mergeStrings : negated conditional → KILLED |
for (int i = 0; i < min; i++) { |
| 223 | char ach = a.charAt(i); | |
| 224 | char bch = b.charAt(i); | |
| 225 |
1
1. mergeStrings : negated conditional → SURVIVED |
if (bch != ' ') |
| 226 | builder.append(bch); | |
| 227 | else | |
| 228 | builder.append(ach); | |
| 229 | } | |
| 230 | // Whichever longer, the result is the same since the shorter.substring(min) will become an empty | |
| 231 | // string. | |
| 232 | builder.append(a.substring(min)); | |
| 233 | builder.append(b.substring(min)); | |
| 234 |
1
1. mergeStrings : replaced return value with "" for com/github/dakusui/actionunit/visitors/ActionReporter::mergeStrings → KILLED |
return builder.toString(); |
| 235 | } | |
| 236 | ||
| 237 | private static boolean isLastChild(Action each, Action parent) { | |
| 238 |
1
1. isLastChild : negated conditional → KILLED |
if (parent instanceof Composite) { |
| 239 | int index = ((Composite) parent).children().indexOf(each); | |
| 240 | int size = ((Composite) parent).children().size(); | |
| 241 | assert index >= 0; | |
| 242 |
3
1. isLastChild : Replaced integer subtraction with addition → KILLED 2. isLastChild : negated conditional → KILLED 3. isLastChild : replaced boolean return with true for com/github/dakusui/actionunit/visitors/ActionReporter::isLastChild → KILLED |
return index == size - 1; |
| 243 | } | |
| 244 |
1
1. isLastChild : replaced boolean return with false for com/github/dakusui/actionunit/visitors/ActionReporter::isLastChild → NO_COVERAGE |
return true; |
| 245 | } | |
| 246 | } | |
Mutations | ||
| 21 |
1.1 2.2 |
|
| 77 |
1.1 |
|
| 82 |
1.1 |
|
| 87 |
1.1 |
|
| 89 |
1.1 |
|
| 90 |
1.1 |
|
| 92 |
1.1 2.2 |
|
| 93 |
1.1 2.2 |
|
| 94 |
1.1 |
|
| 96 |
1.1 |
|
| 99 |
1.1 |
|
| 123 |
1.1 |
|
| 124 |
1.1 |
|
| 126 |
1.1 |
|
| 127 |
1.1 |
|
| 128 |
1.1 |
|
| 133 |
1.1 |
|
| 143 |
1.1 |
|
| 148 |
1.1 |
|
| 149 |
1.1 |
|
| 151 |
1.1 2.2 3.3 |
|
| 152 |
1.1 |
|
| 153 |
1.1 |
|
| 160 |
1.1 |
|
| 161 |
1.1 |
|
| 162 |
1.1 |
|
| 163 |
1.1 |
|
| 164 |
1.1 |
|
| 168 |
1.1 2.2 3.3 |
|
| 172 |
1.1 |
|
| 181 |
1.1 2.2 |
|
| 182 |
1.1 |
|
| 184 |
1.1 |
|
| 190 |
1.1 |
|
| 191 |
1.1 |
|
| 192 |
1.1 |
|
| 196 |
1.1 2.2 |
|
| 222 |
1.1 2.2 3.3 |
|
| 225 |
1.1 |
|
| 234 |
1.1 |
|
| 238 |
1.1 |
|
| 242 |
1.1 2.2 3.3 |
|
| 244 |
1.1 |