• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 11963246771

21 Nov 2024 11:10PM UTC coverage: 67.428% (+0.2%) from 67.239%
11963246771

push

github

web-flow
#754: bullet proof solution for processable output and logging (#777)

2487 of 4028 branches covered (61.74%)

Branch coverage included in aggregate %.

6454 of 9232 relevant lines covered (69.91%)

3.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.49
cli/src/main/java/com/devonfw/tools/ide/step/StepImpl.java
1
package com.devonfw.tools.ide.step;
2

3
import java.time.Duration;
4
import java.util.ArrayList;
5
import java.util.Arrays;
6
import java.util.List;
7

8
import com.devonfw.tools.ide.context.AbstractIdeContext;
9
import com.devonfw.tools.ide.context.IdeContext;
10
import com.devonfw.tools.ide.log.IdeSubLogger;
11

12
/**
13
 * Regular implementation of {@link Step}.
14
 */
15
public final class StepImpl implements Step {
1✔
16

17
  private final AbstractIdeContext context;
18

19
  private final StepImpl parent;
20

21
  private final String name;
22

23
  private final Object[] params;
24

25
  private final List<StepImpl> children;
26

27
  private final long start;
28

29
  private final boolean silent;
30

31
  private Boolean success;
32

33
  private String errorMessage;
34

35
  private long duration;
36

37
  /**
38
   * Creates and starts a new {@link StepImpl}.
39
   *
40
   * @param context the {@link IdeContext}.
41
   * @param parent the {@link #getParent() parent step}.
42
   * @param name the {@link #getName() step name}.
43
   * @param silent the {@link #isSilent() silent flag}.
44
   * @param params the parameters. Should have reasonable {@link Object#toString() string representations}.
45
   */
46
  public StepImpl(AbstractIdeContext context, StepImpl parent, String name, boolean silent, Object... params) {
47

48
    super();
2✔
49
    this.context = context;
3✔
50
    this.parent = parent;
3✔
51
    this.name = name;
3✔
52
    this.params = params;
3✔
53
    this.silent = silent;
3✔
54
    this.children = new ArrayList<>();
5✔
55
    this.start = System.currentTimeMillis();
3✔
56
    if (parent != null) {
2✔
57
      parent.children.add(this);
5✔
58
    }
59
    if (params.length == 0) {
3✔
60
      this.context.trace("Starting step {}...", name);
11✔
61
    } else {
62
      this.context.trace("Starting step {} with params {}...", name, Arrays.toString(params));
15✔
63
    }
64
    if (!this.silent) {
3✔
65
      this.context.step("Start: {}", name);
10✔
66
    }
67
  }
1✔
68

69
  @Override
70
  public StepImpl getParent() {
71

72
    return this.parent;
3✔
73
  }
74

75
  @Override
76
  public String getName() {
77

78
    return this.name;
×
79
  }
80

81
  @Override
82
  public Object getParameter(int i) {
83

84
    if ((i < 0) || (i >= this.params.length)) {
7!
85
      return null;
2✔
86
    }
87
    return this.params[i];
5✔
88
  }
89

90
  @Override
91
  public int getParameterCount() {
92

93
    return this.params.length;
4✔
94
  }
95

96
  @Override
97
  public boolean isSilent() {
98

99
    return this.silent;
×
100
  }
101

102
  @Override
103
  public long getDuration() {
104

105
    return this.duration;
3✔
106
  }
107

108
  @Override
109
  public Boolean getSuccess() {
110

111
    return this.success;
3✔
112
  }
113

114
  @Override
115
  public void success(String message, Object... args) {
116

117
    end(Boolean.TRUE, null, false, message, args);
7✔
118
  }
1✔
119

120
  @Override
121
  public void error(Throwable error, boolean suppress, String message, Object... args) {
122

123
    end(Boolean.FALSE, error, suppress, message, args);
7✔
124
  }
1✔
125

126
  @Override
127
  public void close() {
128

129
    end(null, null, false, null, null);
7✔
130
  }
1✔
131

132
  private void end(Boolean newSuccess, Throwable error, boolean suppress, String message, Object[] args) {
133

134
    boolean firstCallOfEnd = (this.success == null);
7✔
135
    if (!firstCallOfEnd) {
2✔
136
      assert (this.duration > 0);
6!
137
      if (newSuccess != null) {
2✔
138
        this.context.warning("Step '{}' already ended with {} and now ended again with {}.", this.name, this.success, newSuccess);
21✔
139
      } else {
140
        return;
1✔
141
      }
142
    }
143
    long delay = System.currentTimeMillis() - this.start;
5✔
144
    if (delay == 0) {
4✔
145
      delay = 1;
2✔
146
    }
147
    if (newSuccess == null) {
2!
148
      newSuccess = Boolean.FALSE;
×
149
    }
150
    if (!Boolean.FALSE.equals(this.success)) { // never allow a failed step to change to success
5✔
151
      this.duration = delay;
3✔
152
      this.success = newSuccess;
3✔
153
    }
154
    if (newSuccess.booleanValue()) {
3✔
155
      assert (error == null);
3!
156
      if (message != null) {
2✔
157
        this.context.success(message, args);
6✔
158
      } else if (!this.silent) {
3✔
159
        this.context.success("Successfully ended step '{}'.", this.name);
11✔
160
      }
161
      this.context.debug("Step '{}' ended successfully.", this.name);
12✔
162
    } else {
163
      IdeSubLogger logger;
164
      if ((message != null) || (error != null)) {
4!
165
        if (suppress) {
2✔
166
          if (error != null) {
2!
167
            this.errorMessage = error.toString();
5✔
168
          } else {
169
            this.errorMessage = message;
×
170
          }
171
        } else {
172
          if (message == null) {
2✔
173
            message = error.toString();
3✔
174
          }
175
          this.errorMessage = this.context.error().log(error, message, args);
9✔
176
        }
177
        logger = this.context.debug();
5✔
178
      } else {
179
        logger = this.context.info();
×
180
      }
181
      logger.log("Step '{}' ended with failure.", this.name);
11✔
182
    }
183
    if (firstCallOfEnd) {
2✔
184
      this.context.endStep(this);
4✔
185
    }
186
  }
1✔
187

188
  /**
189
   * Logs the summary of this {@link Step}. Should typically only be called on the top-level {@link Step}.
190
   *
191
   * @param suppressSuccess - {@code true} to suppress the success message, {@code false} otherwise.
192
   */
193
  public void logSummary(boolean suppressSuccess) {
194

195
    if (this.context.trace().isEnabled()) {
5!
196
      this.context.trace(toString());
5✔
197
    }
198
    if (this.context.isQuietMode() || (this.children.isEmpty())) {
8!
199
      return;
1✔
200
    }
201
    StepSummary summary = new StepSummary();
4✔
202
    logErrorSummary(0, summary);
4✔
203
    if (summary.getError() == 0) {
3!
204
      if (!suppressSuccess) {
2!
205
        this.context.success("Successfully completed {}", getNameWithParams());
12✔
206
      }
207
    } else {
208
      this.context.error(summary.toString());
×
209
    }
210
  }
1✔
211

212
  private void logErrorSummary(int depth, StepSummary summary) {
213

214
    boolean failure = isFailure();
3✔
215
    summary.add(failure);
3✔
216
    if (failure) {
2!
217
      String error = this.errorMessage;
×
218
      if (error == null) {
×
219
        error = "unexpected error";
×
220
      }
221
      this.context.error("{}Step '{}' failed: {}", getIndent(depth), getNameWithParams(), error);
×
222
    }
223
    depth++;
1✔
224
    for (StepImpl child : this.children) {
11✔
225
      child.logErrorSummary(depth, summary);
4✔
226
    }
1✔
227
  }
1✔
228

229
  private String getNameWithParams() {
230

231
    if ((this.params == null) || (this.params.length == 0)) {
7!
232
      return this.name;
×
233
    }
234
    StringBuilder sb = new StringBuilder(this.name.length() + 3 + this.params.length * 6);
15✔
235
    getNameWithParams(sb);
3✔
236
    return sb.toString();
3✔
237
  }
238

239
  private void getNameWithParams(StringBuilder sb) {
240

241
    sb.append(this.name);
5✔
242
    sb.append(" (");
4✔
243
    String seperator = "";
2✔
244
    if (this.params != null) {
3!
245
      for (Object param : this.params) {
17✔
246
        sb.append(seperator);
4✔
247
        sb.append(param);
4✔
248
        seperator = ",";
2✔
249
      }
250
    }
251
    sb.append(')');
4✔
252
  }
1✔
253

254
  private void append(int depth, long totalDuration, long parentDuration, StringBuilder sb) {
255

256
    // indent
257
    sb.append(getIndent(depth));
6✔
258
    getNameWithParams(sb);
3✔
259
    sb.append(' ');
4✔
260
    if (this.success == null) {
3!
261
      sb.append("is still running or was not properly ended due to programming error not using finally block ");
×
262
    } else {
263
      if (this.success.booleanValue()) {
4!
264
        sb.append("succeeded after ");
5✔
265
      } else {
266
        sb.append("failed after ");
×
267
      }
268
      sb.append(Duration.ofMillis(this.duration));
6✔
269
    }
270
    if (this.duration < totalDuration) {
5✔
271
      sb.append(" ");
4✔
272
      double percentageBase = this.duration * 100;
6✔
273
      double totalPercentage = percentageBase / totalDuration;
5✔
274
      sb.append(totalPercentage);
4✔
275
      sb.append("% of total ");
4✔
276
      if (parentDuration < totalDuration) {
4!
277
        double parentPercentage = percentageBase / parentDuration;
×
278
        sb.append(parentPercentage);
×
279
        sb.append("% of parent");
×
280
      }
281
    }
282
    sb.append('\n');
4✔
283
    int childDepth = depth + 1;
4✔
284
    for (StepImpl child : this.children) {
11✔
285
      child.append(childDepth, totalDuration, this.duration, sb);
7✔
286
    }
1✔
287
  }
1✔
288

289
  private String getIndent(int depth) {
290

291
    return " ".repeat(depth);
4✔
292
  }
293

294
  @Override
295
  public String toString() {
296

297
    StringBuilder sb = new StringBuilder(4096);
5✔
298
    append(0, this.duration, this.duration, sb);
8✔
299
    return sb.toString();
3✔
300
  }
301
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc