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 |