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

devonfw / IDEasy / 15217656699

23 May 2025 07:22PM UTC coverage: 67.562% (-0.3%) from 67.89%
15217656699

push

github

web-flow
#1332: fixed bug pattern, proper Step usage, allow running tool if plugin failed (#1334)

Co-authored-by: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com>

3151 of 5064 branches covered (62.22%)

Branch coverage included in aggregate %.

8048 of 11512 relevant lines covered (69.91%)

3.07 hits per line

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

63.16
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.IdeLogLevel;
11
import com.devonfw.tools.ide.log.IdeSubLogger;
12

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

18
  private final AbstractIdeContext context;
19

20
  private final StepImpl parent;
21

22
  private final String name;
23

24
  private final Object[] params;
25

26
  private final List<StepImpl> children;
27

28
  private final long start;
29

30
  private final boolean silent;
31

32
  private Boolean success;
33

34
  private String errorMessage;
35

36
  private long duration;
37

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

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

70
  @Override
71
  public StepImpl getParent() {
72

73
    return this.parent;
3✔
74
  }
75

76
  @Override
77
  public String getName() {
78

79
    return this.name;
×
80
  }
81

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

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

91
  @Override
92
  public int getParameterCount() {
93

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

97
  @Override
98
  public boolean isSilent() {
99

100
    return this.silent;
×
101
  }
102

103
  @Override
104
  public long getDuration() {
105

106
    return this.duration;
3✔
107
  }
108

109
  @Override
110
  public Boolean getSuccess() {
111

112
    return this.success;
3✔
113
  }
114

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

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

121
  @Override
122
  public IdeSubLogger asSuccess() {
123

124
    return new IdeSubLogger() {
×
125
      @Override
126
      public String log(Throwable error, String message, Object... args) {
127

128
        assert (error == null);
×
129
        success(message, args);
×
130
        return message;
×
131
      }
132

133
      @Override
134
      public boolean isEnabled() {
135

136
        return true;
×
137
      }
138

139
      @Override
140
      public IdeLogLevel getLevel() {
141

142
        return IdeLogLevel.SUCCESS;
×
143
      }
144
    };
145
  }
146

147
  @Override
148
  public void error(Throwable error, boolean suppress, String message, Object... args) {
149

150
    end(Boolean.FALSE, error, suppress, message, args);
7✔
151
  }
1✔
152

153
  @Override
154
  public IdeSubLogger asError() {
155

156
    return new IdeSubLogger() {
×
157
      @Override
158
      public String log(Throwable error, String message, Object... args) {
159

160
        error(error, message, args);
×
161
        return message;
×
162
      }
163

164
      @Override
165
      public boolean isEnabled() {
166

167
        return true;
×
168
      }
169

170
      @Override
171
      public IdeLogLevel getLevel() {
172

173
        return IdeLogLevel.ERROR;
×
174
      }
175
    };
176
  }
177

178
  @Override
179
  public void close() {
180

181
    end(null, null, false, null, null);
7✔
182
  }
1✔
183

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

186
    boolean firstCallOfEnd = (this.success == null);
7✔
187
    if (!firstCallOfEnd) {
2✔
188
      assert (this.duration > 0);
6!
189
      if ((newSuccess != null) && (newSuccess != this.success)) {
6!
190
        this.context.warning("Step '{}' already ended with {} and now ended again with {}.", this.name, this.success, newSuccess);
21✔
191
      } else {
192
        return;
1✔
193
      }
194
    }
195
    long delay = System.currentTimeMillis() - this.start;
5✔
196
    if (delay == 0) {
4✔
197
      delay = 1;
2✔
198
    }
199
    if (newSuccess == null) {
2!
200
      newSuccess = Boolean.FALSE;
×
201
    }
202
    if (!Boolean.FALSE.equals(this.success)) { // never allow a failed step to change to success
5✔
203
      this.duration = delay;
3✔
204
      this.success = newSuccess;
3✔
205
    }
206
    if (newSuccess.booleanValue()) {
3✔
207
      assert (error == null);
3!
208
      if (message != null) {
2✔
209
        this.context.success(message, args);
6✔
210
      } else if (!this.silent) {
3✔
211
        this.context.success("Successfully ended step '{}'.", this.name);
11✔
212
      }
213
      this.context.debug("Step '{}' ended successfully.", this.name);
12✔
214
    } else {
215
      IdeSubLogger logger;
216
      if ((message != null) || (error != null)) {
4!
217
        if (suppress) {
2✔
218
          if (error != null) {
2!
219
            this.errorMessage = error.toString();
5✔
220
          } else {
221
            this.errorMessage = message;
×
222
          }
223
          logger = this.context.debug();
5✔
224
        } else {
225
          this.errorMessage = this.context.error().log(error, message, args);
9✔
226
          if (error == null) {
2✔
227
            logger = this.context.debug();
5✔
228
          } else {
229
            logger = this.context.error();
5✔
230
          }
231
        }
232
      } else {
233
        logger = this.context.info();
×
234
      }
235
      logger.log("Step '{}' ended with failure.", this.name);
11✔
236
    }
237
    if (firstCallOfEnd) {
2✔
238
      this.context.endStep(this);
4✔
239
    }
240
  }
1✔
241

242
  /**
243
   * Logs the summary of this {@link Step}. Should typically only be called on the top-level {@link Step}.
244
   *
245
   * @param suppressSuccess - {@code true} to suppress the success message, {@code false} otherwise.
246
   */
247
  public void logSummary(boolean suppressSuccess) {
248

249
    if (this.context.trace().isEnabled()) {
5!
250
      this.context.trace(toString());
5✔
251
    }
252
    if (this.context.isQuietMode() || (this.children.isEmpty())) {
8!
253
      return;
1✔
254
    }
255
    StepSummary summary = new StepSummary();
×
256
    logErrorSummary(0, summary);
×
257
    if (summary.getError() == 0) {
×
258
      if (!suppressSuccess) {
×
259
        this.context.success("Successfully completed {}", getNameWithParams());
×
260
      }
261
    } else {
262
      this.context.error(summary.toString());
×
263
    }
264
  }
×
265

266
  private void logErrorSummary(int depth, StepSummary summary) {
267

268
    boolean failure = isFailure();
×
269
    summary.add(failure);
×
270
    if (failure) {
×
271
      String error = this.errorMessage;
×
272
      if (error == null) {
×
273
        error = "unexpected error";
×
274
      }
275
      this.context.error("{}Step '{}' failed: {}", getIndent(depth), getNameWithParams(), error);
×
276
    }
277
    depth++;
×
278
    for (StepImpl child : this.children) {
×
279
      child.logErrorSummary(depth, summary);
×
280
    }
×
281
  }
×
282

283
  private String getNameWithParams() {
284

285
    if ((this.params == null) || (this.params.length == 0)) {
×
286
      return this.name;
×
287
    }
288
    StringBuilder sb = new StringBuilder(this.name.length() + 3 + this.params.length * 6);
×
289
    getNameWithParams(sb);
×
290
    return sb.toString();
×
291
  }
292

293
  private void getNameWithParams(StringBuilder sb) {
294

295
    sb.append(this.name);
5✔
296
    sb.append(" (");
4✔
297
    String seperator = "";
2✔
298
    if (this.params != null) {
3!
299
      for (Object param : this.params) {
17✔
300
        sb.append(seperator);
4✔
301
        sb.append(param);
4✔
302
        seperator = ",";
2✔
303
      }
304
    }
305
    sb.append(')');
4✔
306
  }
1✔
307

308
  private void append(int depth, long totalDuration, long parentDuration, StringBuilder sb) {
309

310
    // indent
311
    sb.append(getIndent(depth));
6✔
312
    getNameWithParams(sb);
3✔
313
    sb.append(' ');
4✔
314
    if (this.success == null) {
3!
315
      sb.append("is still running or was not properly ended due to programming error not using finally block ");
×
316
    } else {
317
      if (this.success.booleanValue()) {
4✔
318
        sb.append("succeeded after ");
5✔
319
      } else {
320
        sb.append("failed after ");
4✔
321
      }
322
      sb.append(Duration.ofMillis(this.duration));
6✔
323
    }
324
    if (this.duration < totalDuration) {
5!
325
      sb.append(" ");
×
326
      double percentageBase = this.duration * 100;
×
327
      double totalPercentage = percentageBase / totalDuration;
×
328
      sb.append(totalPercentage);
×
329
      sb.append("% of total ");
×
330
      if (parentDuration < totalDuration) {
×
331
        double parentPercentage = percentageBase / parentDuration;
×
332
        sb.append(parentPercentage);
×
333
        sb.append("% of parent");
×
334
      }
335
    }
336
    sb.append('\n');
4✔
337
    int childDepth = depth + 1;
4✔
338
    for (StepImpl child : this.children) {
7!
339
      child.append(childDepth, totalDuration, this.duration, sb);
×
340
    }
×
341
  }
1✔
342

343
  private String getIndent(int depth) {
344

345
    return " ".repeat(depth);
4✔
346
  }
347

348
  @Override
349
  public String toString() {
350

351
    StringBuilder sb = new StringBuilder(4096);
5✔
352
    append(0, this.duration, this.duration, sb);
8✔
353
    return sb.toString();
3✔
354
  }
355
}
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

© 2026 Coveralls, Inc