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

devonfw / IDEasy / 8144002629

04 Mar 2024 04:56PM UTC coverage: 58.928% (+0.7%) from 58.254%
8144002629

push

github

web-flow
#208: improve test infrastructure # 219: fix wrong executable (#238)

* Add first implementation

* Add javadoc

* Add javadoc

* Using target path instead of ressource path to make sure one-to-one copy of set up folders is used

* Add JavaDoc and mac mock program

* Add support for multiple dependencies while testing

* Minor changes

* Modify mock programs for testing

* Refactored example JmcTest

* Add possibility to set execution path by using the context

* Reenable test after related issue 228 has been merged

* Replace ternary with regular if

* Add missing javadoc

* Add missing javadoc

* remove unnecessary semicolon

* Fix spelling typo

* Minor test changes

* Refactoring FileExtractor class for more modularity

* using const

* Add missing extensions

* Remove unnecessary execption declaration and minor rename

* Fix spelling

* Add javadoc

* Forget dot

* minor change

* Revert "minor change"

This reverts commit ec81c3ce6.

* Revert "Merge branch 'main' into feature/208-MockOutToolRepoRefactorTestInfra"

This reverts commit d58847230, reversing
changes made to f38b3105f.

* Revert "Revert "Merge branch 'main' into feature/208-MockOutToolRepoRefactorTestInfra""

This reverts commit 3e49a0b3d.

* Revert "Revert "minor change""

This reverts commit 2f7b94624.

* fix typo

* #208: review and complete rework

* #208: improved system path

* #208: found and fixed bug on windows

* #208: improve OS mocking

* #208: improve test logging

reveal logs/errors so the developer actually sees what is happening instead of leaving them in the dark

* #208: improve OS detection

* #208: fixed

found and fixed bug in MacOS app detection for JMC, fixed copy file to folder bug, moved !extract logic back to FileAccess, f... (continued)

1580 of 2930 branches covered (53.92%)

Branch coverage included in aggregate %.

4047 of 6619 relevant lines covered (61.14%)

2.65 hits per line

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

67.68
cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java
1
package com.devonfw.tools.ide.tool;
2

3
import com.devonfw.tools.ide.commandlet.Commandlet;
4
import com.devonfw.tools.ide.common.Tag;
5
import com.devonfw.tools.ide.common.Tags;
6
import com.devonfw.tools.ide.context.IdeContext;
7
import com.devonfw.tools.ide.environment.EnvironmentVariables;
8
import com.devonfw.tools.ide.environment.EnvironmentVariablesType;
9
import com.devonfw.tools.ide.os.MacOsHelper;
10
import com.devonfw.tools.ide.process.ProcessContext;
11
import com.devonfw.tools.ide.process.ProcessErrorHandling;
12
import com.devonfw.tools.ide.process.ProcessMode;
13
import com.devonfw.tools.ide.property.StringListProperty;
14
import com.devonfw.tools.ide.version.VersionIdentifier;
15

16
import java.io.IOException;
17
import java.nio.file.Files;
18
import java.nio.file.Path;
19
import java.util.List;
20
import java.util.Set;
21

22
/**
23
 * {@link Commandlet} for a tool integrated into the IDE.
24
 */
25
public abstract class ToolCommandlet extends Commandlet implements Tags {
26

27
  /** @see #getName() */
28
  protected final String tool;
29

30
  private final Set<Tag> tags;
31

32
  /** The commandline arguments to pass to the tool. */
33
  public final StringListProperty arguments;
34

35
  private MacOsHelper macOsHelper;
36

37
  /**
38
   * The constructor.
39
   *
40
   * @param context the {@link IdeContext}.
41
   * @param tool the {@link #getName() tool name}.
42
   * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of}
43
   * method.
44
   */
45
  public ToolCommandlet(IdeContext context, String tool, Set<Tag> tags) {
46

47
    super(context);
3✔
48
    this.tool = tool;
3✔
49
    this.tags = tags;
3✔
50
    addKeyword(tool);
3✔
51
    this.arguments = add(new StringListProperty("", false, "args"));
11✔
52
  }
1✔
53

54
  /**
55
   * @return the name of the tool (e.g. "java", "mvn", "npm", "node").
56
   */
57
  @Override
58
  public String getName() {
59

60
    return this.tool;
3✔
61
  }
62

63
  /**
64
   * @return the name of the binary executable for this tool.
65
   */
66
  protected String getBinaryName() {
67

68
    return this.tool;
3✔
69
  }
70

71
  @Override
72
  public final Set<Tag> getTags() {
73

74
    return this.tags;
×
75
  }
76

77
  @Override
78
  public void run() {
79

80
    runTool(ProcessMode.DEFAULT, null, this.arguments.asArray());
×
81
  }
×
82

83
  /**
84
   * Ensures the tool is installed and then runs this tool with the given arguments.
85
   *
86
   * @param processMode see {@link ProcessMode}
87
   * @param toolVersion the explicit version (pattern) to run. Typically {@code null} to ensure the configured version
88
   * is installed and use that one. Otherwise, the specified version will be installed in the software repository
89
   * without touching and IDE installation and used to run.
90
   * @param args the command-line arguments to run the tool.
91
   */
92
  public void runTool(ProcessMode processMode, VersionIdentifier toolVersion, String... args) {
93

94
    Path binaryPath;
95
    Path toolPath = Path.of(getBinaryName());
6✔
96
    if (toolVersion == null) {
2!
97
      install(true);
4✔
98
      binaryPath = toolPath;
3✔
99
    } else {
100
      throw new UnsupportedOperationException("Not yet implemented!");
×
101
    }
102
    ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.WARNING).executable(binaryPath)
8✔
103
        .addArgs(args);
2✔
104

105
    pc.run(processMode);
4✔
106
  }
1✔
107

108
  /**
109
   * @param toolVersion the explicit {@link VersionIdentifier} of the tool to run.
110
   * @param args the command-line arguments to run the tool.
111
   * @see ToolCommandlet#runTool(ProcessMode, VersionIdentifier, String...)
112
   */
113
  public void runTool(VersionIdentifier toolVersion, String... args) {
114

115
    runTool(ProcessMode.DEFAULT, toolVersion, args);
×
116
  }
×
117

118
  /**
119
   * @return the {@link EnvironmentVariables#getToolEdition(String) tool edition}.
120
   */
121
  public String getEdition() {
122

123
    return this.context.getVariables().getToolEdition(getName());
7✔
124
  }
125

126
  /**
127
   * @return the {@link #getName() tool} with its {@link #getEdition() edition}. The edition will be omitted if same as
128
   * tool.
129
   * @see #getToolWithEdition(String, String)
130
   */
131
  protected final String getToolWithEdition() {
132

133
    return getToolWithEdition(getName(), getEdition());
×
134
  }
135

136
  /**
137
   * @param tool the tool name.
138
   * @param edition the edition.
139
   * @return the {@link #getName() tool} with its {@link #getEdition() edition}. The edition will be omitted if same as
140
   * tool.
141
   */
142
  protected final static String getToolWithEdition(String tool, String edition) {
143

144
    if (tool.equals(edition)) {
×
145
      return tool;
×
146
    }
147
    return tool + "/" + edition;
×
148
  }
149

150
  /**
151
   * @return the {@link EnvironmentVariables#getToolVersion(String) tool version}.
152
   */
153
  public VersionIdentifier getConfiguredVersion() {
154

155
    return this.context.getVariables().getToolVersion(getName());
7✔
156
  }
157

158
  /**
159
   * Method to be called for {@link #install(boolean)} from dependent
160
   * {@link com.devonfw.tools.ide.commandlet.Commandlet}s.
161
   *
162
   * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and
163
   * nothing has changed.
164
   */
165
  public boolean install() {
166

167
    return install(true);
4✔
168
  }
169

170
  /**
171
   * Performs the installation of the {@link #getName() tool} managed by this
172
   * {@link com.devonfw.tools.ide.commandlet.Commandlet}.
173
   *
174
   * @param silent - {@code true} if called recursively to suppress verbose logging, {@code false} otherwise.
175
   * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and
176
   * nothing has changed.
177
   */
178
  public boolean install(boolean silent) {
179

180
    return doInstall(silent);
4✔
181
  }
182

183
  /**
184
   * Installs or updates the managed {@link #getName() tool}.
185
   *
186
   * @param silent - {@code true} if called recursively to suppress verbose logging, {@code false} otherwise.
187
   * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and
188
   * nothing has changed.
189
   */
190
  protected abstract boolean doInstall(boolean silent);
191

192
  /**
193
   * This method is called after the tool has been newly installed or updated to a new version.
194
   */
195
  protected void postInstall() {
196

197
    // nothing to do by default
198
  }
1✔
199

200
  /**
201
   * @return {@code true} to extract (unpack) the downloaded binary file, {@code false} otherwise.
202
   */
203
  protected boolean isExtract() {
204

205
    return true;
2✔
206
  }
207

208
  /**
209
   * @return the {@link MacOsHelper} instance.
210
   */
211
  protected MacOsHelper getMacOsHelper() {
212

213
    if (this.macOsHelper == null) {
3!
214
      this.macOsHelper = new MacOsHelper(this.context);
7✔
215
    }
216
    return this.macOsHelper;
3✔
217
  }
218

219
  /**
220
   * @return the currently installed {@link VersionIdentifier version} of this tool or {@code null} if not installed.
221
   */
222
  public VersionIdentifier getInstalledVersion() {
223

224
    return getInstalledVersion(this.context.getSoftwarePath().resolve(getName()));
9✔
225
  }
226

227
  /**
228
   * @param toolPath the installation {@link Path} where to find the version file.
229
   * @return the currently installed {@link VersionIdentifier version} of this tool or {@code null} if not installed.
230
   */
231
  protected VersionIdentifier getInstalledVersion(Path toolPath) {
232

233
    if (!Files.isDirectory(toolPath)) {
5✔
234
      this.context.debug("Tool {} not installed in {}", getName(), toolPath);
15✔
235
      return null;
2✔
236
    }
237
    Path toolVersionFile = toolPath.resolve(IdeContext.FILE_SOFTWARE_VERSION);
4✔
238
    if (!Files.exists(toolVersionFile)) {
5✔
239
      Path legacyToolVersionFile = toolPath.resolve(IdeContext.FILE_LEGACY_SOFTWARE_VERSION);
4✔
240
      if (Files.exists(legacyToolVersionFile)) {
5✔
241
        toolVersionFile = legacyToolVersionFile;
3✔
242
      } else {
243
        this.context.warning("Tool {} is missing version file in {}", getName(), toolVersionFile);
15✔
244
        return null;
2✔
245
      }
246
    }
247
    try {
248
      String version = Files.readString(toolVersionFile).trim();
4✔
249
      return VersionIdentifier.of(version);
3✔
250
    } catch (IOException e) {
×
251
      throw new IllegalStateException("Failed to read file " + toolVersionFile, e);
×
252
    }
253
  }
254

255
  /**
256
   * @return the installed edition of this tool or {@code null} if not installed.
257
   */
258
  public String getInstalledEdition() {
259

260
    return getInstalledEdition(this.context.getSoftwarePath().resolve(getName()));
9✔
261
  }
262

263
  /**
264
   * @param toolPath the installation {@link Path} where to find currently installed tool. The name of the parent
265
   * directory of the real path corresponding to the passed {@link Path path} must be the name of the edition.
266
   * @return the installed edition of this tool or {@code null} if not installed.
267
   */
268
  public String getInstalledEdition(Path toolPath) {
269

270
    if (!Files.isDirectory(toolPath)) {
5!
271
      this.context.debug("Tool {} not installed in {}", getName(), toolPath);
×
272
      return null;
×
273
    }
274
    try {
275
      String edition = toolPath.toRealPath().getParent().getFileName().toString();
8✔
276
      if (!this.context.getUrls().getSortedEditions(getName()).contains(edition)) {
9!
277
        edition = getEdition();
3✔
278
      }
279
      return edition;
2✔
280
    } catch (IOException e) {
×
281
      throw new IllegalStateException(
×
282
          "Couldn't determine the edition of " + getName() + " from the directory structure of its software path "
×
283
              + toolPath
284
              + ", assuming the name of the parent directory of the real path of the software path to be the edition "
285
              + "of the tool.", e);
286
    }
287

288
  }
289

290
  /**
291
   * List the available editions of this tool.
292
   */
293
  public void listEditions() {
294

295
    List<String> editions = this.context.getUrls().getSortedEditions(getName());
7✔
296
    for (String edition : editions) {
10✔
297
      this.context.info(edition);
4✔
298
    }
1✔
299
  }
1✔
300

301
  /**
302
   * List the available versions of this tool.
303
   */
304
  public void listVersions() {
305

306
    List<VersionIdentifier> versions = this.context.getUrls().getSortedVersions(getName(), getEdition());
9✔
307
    for (VersionIdentifier vi : versions) {
10✔
308
      this.context.info(vi.toString());
5✔
309
    }
1✔
310
  }
1✔
311

312
  /**
313
   * Sets the tool version in the environment variable configuration file.
314
   *
315
   * @param version the version (pattern) to set.
316
   */
317
  public void setVersion(String version) {
318

319
    if ((version == null) || version.isBlank()) {
×
320
      throw new IllegalStateException("Version has to be specified!");
×
321
    }
322
    VersionIdentifier configuredVersion = VersionIdentifier.of(version);
×
323
    if (!configuredVersion.isPattern() && !configuredVersion.isValid()) {
×
324
      this.context.warning("Version {} seems to be invalid", version);
×
325
    }
326
    setVersion(configuredVersion, true);
×
327
  }
×
328

329
  /**
330
   * Sets the tool version in the environment variable configuration file.
331
   *
332
   * @param version the version to set. May also be a {@link VersionIdentifier#isPattern() version pattern}.
333
   * @param hint - {@code true} to print the installation hint, {@code false} otherwise.
334
   */
335
  public void setVersion(VersionIdentifier version, boolean hint) {
336

337
    EnvironmentVariables variables = this.context.getVariables();
4✔
338
    EnvironmentVariables settingsVariables = variables.getByType(EnvironmentVariablesType.SETTINGS);
4✔
339
    String edition = getEdition();
3✔
340
    String name = EnvironmentVariables.getToolVersionVariable(this.tool);
4✔
341
    VersionIdentifier resolvedVersion = this.context.getUrls().getVersion(this.tool, edition, version);
9✔
342
    if (version.isPattern()) {
3!
343
      this.context.debug("Resolved version {} to {} for tool {}/{}", version, resolvedVersion, this.tool, edition);
×
344
    }
345
    settingsVariables.set(name, resolvedVersion.toString(), false);
7✔
346
    settingsVariables.save();
2✔
347
    this.context.info("{}={} has been set in {}", name, version, settingsVariables.getSource());
19✔
348
    EnvironmentVariables declaringVariables = variables.findVariable(name);
4✔
349
    if ((declaringVariables != null) && (declaringVariables != settingsVariables)) {
5!
350
      this.context.warning(
×
351
          "The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.",
352
          name, declaringVariables.getSource());
×
353
    }
354
    if (hint) {
2✔
355
      this.context.info("To install that version call the following command:");
4✔
356
      this.context.info("ide install {}", this.tool);
11✔
357
    }
358
  }
1✔
359

360
  /**
361
   * Sets the tool edition in the environment variable configuration file.
362
   *
363
   * @param edition the edition to set.
364
   */
365
  public void setEdition(String edition) {
366

367
    setEdition(edition, true);
4✔
368
  }
1✔
369

370
  /**
371
   * Sets the tool edition in the environment variable configuration file.
372
   *
373
   * @param edition the edition to set
374
   * @param hint - {@code true} to print the installation hint, {@code false} otherwise.
375
   */
376
  public void setEdition(String edition, boolean hint) {
377

378
    if ((edition == null) || edition.isBlank()) {
5!
379
      throw new IllegalStateException("Edition has to be specified!");
×
380
    }
381

382
    if (!Files.exists(this.context.getUrls().getEdition(getName(), edition).getPath())) {
12!
383
      this.context.warning("Edition {} seems to be invalid", edition);
10✔
384

385
    }
386
    EnvironmentVariables variables = this.context.getVariables();
4✔
387
    EnvironmentVariables settingsVariables = variables.getByType(EnvironmentVariablesType.SETTINGS);
4✔
388
    String name = EnvironmentVariables.getToolEditionVariable(this.tool);
4✔
389
    settingsVariables.set(name, edition, false);
6✔
390
    settingsVariables.save();
2✔
391

392
    this.context.info("{}={} has been set in {}", name, edition, settingsVariables.getSource());
19✔
393
    EnvironmentVariables declaringVariables = variables.findVariable(name);
4✔
394
    if ((declaringVariables != null) && (declaringVariables != settingsVariables)) {
5!
395
      this.context.warning(
×
396
          "The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.",
397
          name, declaringVariables.getSource());
×
398
    }
399
    if (hint) {
2!
400
      this.context.info("To install that edition call the following command:");
4✔
401
      this.context.info("ide install {}", this.tool);
11✔
402
    }
403
  }
1✔
404

405
}
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