Skip to content

Commit bb37bd1

Browse files
hohwilleMustaphaOuchenMuOuchen
authored
#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 ec81c3c. * Revert "Merge branch 'main' into feature/208-MockOutToolRepoRefactorTestInfra" This reverts commit d588472, reversing changes made to f38b310. * Revert "Revert "Merge branch 'main' into feature/208-MockOutToolRepoRefactorTestInfra"" This reverts commit 3e49a0b. * Revert "Revert "minor change"" This reverts commit 2f7b946. * 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, fixed .tar.gz/bz2 handling bug, etc. * #208: fixed TarCompression * removed broken doc * fixed table syntax * removed totally misplaced import to logback Configurator * #219: fix EXTENSION_PRIORITY * prevent log spam * #208: fixed OS problem executing CMD files on linux or mac * use interpreter workaround only on Windows * set X flags in Git for bash scripts * now remove windows exclusion as it should again work also on other OS --------- Co-authored-by: Ouchen <[email protected]> Co-authored-by: MustaphaOuchen <[email protected]> Co-authored-by: mouchen <[email protected]>
1 parent 2df8b88 commit bb37bd1

File tree

101 files changed

+7831
-7281
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+7831
-7281
lines changed

cli/pom.xml

+257-255
Large diffs are not rendered by default.

cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java

+60-28
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package com.devonfw.tools.ide.common;
22

3+
import com.devonfw.tools.ide.context.IdeContext;
4+
import com.devonfw.tools.ide.os.SystemInfoImpl;
5+
import com.devonfw.tools.ide.variable.IdeVariables;
6+
37
import java.io.File;
48
import java.io.IOException;
59
import java.nio.file.Files;
@@ -12,10 +16,17 @@
1216
import java.util.regex.Pattern;
1317
import java.util.stream.Stream;
1418

15-
import com.devonfw.tools.ide.context.IdeContext;
16-
1719
/**
18-
* Represents the PATH variable in a structured way.
20+
* Represents the PATH variable in a structured way. The PATH contains the system path entries together with the entries
21+
* for the IDEasy tools. The generic system path entries are stored in a {@link List} ({@code paths}) and the tool
22+
* entries are stored in a {@link Map} ({@code tool2pathMap}) as they can change dynamically at runtime (e.g. if a new
23+
* tool is installed). As the tools must have priority the actual PATH is build by first the entries for the tools and
24+
* then the generic entries from the system PATH. Such tool entries are ignored from the actual PATH of the
25+
* {@link System#getenv(String) environment} at construction time and are recomputed from the "software" folder. This is
26+
* important as the initial {@link System#getenv(String) environment} PATH entries can come from a different IDEasy
27+
* project and the use may have changed projects before calling us again. Recomputing the PATH ensures side-effects from
28+
* other projects. However, it also will ensure all the entries to IDEasy locations are automatically managed and
29+
* therefore cannot be managed manually be the end-user.
1930
*/
2031
public class SystemPath {
2132

@@ -34,24 +45,47 @@ public class SystemPath {
3445
/**
3546
* The constructor.
3647
*
48+
* @param context {@link IdeContext}.
49+
*/
50+
public SystemPath(IdeContext context) {
51+
52+
this(context, System.getenv(IdeVariables.PATH.getName()));
53+
}
54+
55+
/**
56+
* The constructor.
57+
*
58+
* @param context {@link IdeContext}.
3759
* @param envPath the value of the PATH variable.
38-
* @param softwarePath the {@link IdeContext#getSoftwarePath() software path}.
60+
*/
61+
public SystemPath(IdeContext context, String envPath) {
62+
63+
this(context, envPath, context.getIdeRoot(), context.getSoftwarePath());
64+
}
65+
66+
/**
67+
* The constructor.
68+
*
3969
* @param context {@link IdeContext} for the output of information.
70+
* @param envPath the value of the PATH variable.
71+
* @param ideRoot the {@link IdeContext#getIdeRoot() IDE_ROOT}.
72+
* @param softwarePath the {@link IdeContext#getSoftwarePath() software path}.
4073
*/
41-
public SystemPath(String envPath, Path softwarePath, IdeContext context) {
74+
public SystemPath(IdeContext context, String envPath, Path ideRoot, Path softwarePath) {
4275

43-
this(envPath, softwarePath, File.pathSeparatorChar, context);
76+
this(context, envPath, ideRoot, softwarePath, File.pathSeparatorChar);
4477
}
4578

4679
/**
4780
* The constructor.
4881
*
82+
* @param context {@link IdeContext} for the output of information.
4983
* @param envPath the value of the PATH variable.
84+
* @param ideRoot the {@link IdeContext#getIdeRoot() IDE_ROOT}.
5085
* @param softwarePath the {@link IdeContext#getSoftwarePath() software path}.
5186
* @param pathSeparator the path separator char (';' for Windows and ':' otherwise).
52-
* @param context {@link IdeContext} for the output of information.
5387
*/
54-
public SystemPath(String envPath, Path softwarePath, char pathSeparator, IdeContext context) {
88+
public SystemPath(IdeContext context, String envPath, Path ideRoot, Path softwarePath, char pathSeparator) {
5589

5690
super();
5791
this.context = context;
@@ -62,15 +96,9 @@ public SystemPath(String envPath, Path softwarePath, char pathSeparator, IdeCont
6296
String[] envPaths = envPath.split(Character.toString(pathSeparator));
6397
for (String segment : envPaths) {
6498
Path path = Path.of(segment);
65-
String tool = getTool(path, softwarePath);
99+
String tool = getTool(path, ideRoot);
66100
if (tool == null) {
67101
this.paths.add(path);
68-
} else {
69-
Path duplicate = this.tool2pathMap.putIfAbsent(tool, path);
70-
if (duplicate != null) {
71-
context.warning("Duplicated tool path for tool: {} at path: {} with duplicated path: {}.", tool, path,
72-
duplicate);
73-
}
74102
}
75103
}
76104
collectToolPath(softwarePath);
@@ -86,14 +114,14 @@ private void collectToolPath(Path softwarePath) {
86114
Iterator<Path> iterator = children.iterator();
87115
while (iterator.hasNext()) {
88116
Path child = iterator.next();
89-
if (Files.isDirectory(child)) {
117+
String tool = child.getFileName().toString();
118+
if (!"extra".equals(tool) && Files.isDirectory(child)) {
90119
Path toolPath = child;
91120
Path bin = child.resolve("bin");
92121
if (Files.isDirectory(bin)) {
93122
toolPath = bin;
94123
}
95-
this.paths.add(0, toolPath);
96-
this.tool2pathMap.put(child.getFileName().toString(), toolPath);
124+
this.tool2pathMap.put(tool, toolPath);
97125
}
98126
}
99127
} catch (IOException e) {
@@ -102,13 +130,13 @@ private void collectToolPath(Path softwarePath) {
102130
}
103131
}
104132

105-
private static String getTool(Path path, Path softwarePath) {
133+
private static String getTool(Path path, Path ideRoot) {
106134

107-
if (softwarePath == null) {
135+
if (ideRoot == null) {
108136
return null;
109137
}
110-
if (path.startsWith(softwarePath)) {
111-
int i = softwarePath.getNameCount();
138+
if (path.startsWith(ideRoot)) {
139+
int i = ideRoot.getNameCount();
112140
if (path.getNameCount() > i) {
113141
return path.getName(i).toString();
114142
}
@@ -118,7 +146,11 @@ private static String getTool(Path path, Path softwarePath) {
118146

119147
private Path findBinaryInOrder(Path path, String tool) {
120148

121-
for (String extension : EXTENSION_PRIORITY) {
149+
List<String> extensionPriority = List.of("");
150+
if (SystemInfoImpl.INSTANCE.isWindows()) {
151+
extensionPriority = EXTENSION_PRIORITY;
152+
}
153+
for (String extension : extensionPriority) {
122154

123155
Path fileToExecute = path.resolve(tool + extension);
124156

@@ -133,7 +165,7 @@ private Path findBinaryInOrder(Path path, String tool) {
133165
/**
134166
* @param toolPath the {@link Path} to the tool installation.
135167
* @return the {@link Path} to the binary executable of the tool. E.g. is "software/mvn" is given
136-
* "software/mvn/bin/mvn" could be returned.
168+
* "software/mvn/bin/mvn" could be returned.
137169
*/
138170
public Path findBinary(Path toolPath) {
139171

@@ -168,7 +200,7 @@ public Path findBinary(Path toolPath) {
168200
/**
169201
* @param tool the name of the tool.
170202
* @return the {@link Path} to the directory of the tool where the binaries can be found or {@code null} if the tool
171-
* is not installed.
203+
* is not installed.
172204
*/
173205
public Path getPath(String tool) {
174206

@@ -192,7 +224,7 @@ public String toString() {
192224

193225
/**
194226
* @param bash - {@code true} to convert the PATH to bash syntax (relevant for git-bash or cygwin on windows),
195-
* {@code false} otherwise.
227+
* {@code false} otherwise.
196228
* @return this {@link SystemPath} as {@link String} for the PATH environment variable.
197229
*/
198230
public String toString(boolean bash) {
@@ -227,7 +259,7 @@ private static void appendPath(Path path, StringBuilder sb, char separator, bool
227259

228260
/**
229261
* Method to convert a valid Windows path string representation to its corresponding one in Unix format.
230-
*
262+
*
231263
* @param pathString The Windows path string to convert.
232264
* @return The converted Unix path string.
233265
*/
@@ -245,7 +277,7 @@ public static String convertWindowsPathToUnixPath(String pathString) {
245277

246278
/**
247279
* Method to validate if a given path string is a Windows path or not
248-
*
280+
*
249281
* @param pathString The string to check if it is a Windows path string.
250282
* @return {@code true} if it is a valid windows path string, else {@code false}.
251283
*/

cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java

+68-32
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
package com.devonfw.tools.ide.context;
22

3-
import java.io.IOException;
4-
import java.net.InetAddress;
5-
import java.nio.file.Files;
6-
import java.nio.file.Path;
7-
import java.util.HashMap;
8-
import java.util.Iterator;
9-
import java.util.List;
10-
import java.util.Locale;
11-
import java.util.Map;
12-
import java.util.Objects;
13-
import java.util.function.Function;
14-
153
import com.devonfw.tools.ide.cli.CliArgument;
164
import com.devonfw.tools.ide.cli.CliArguments;
175
import com.devonfw.tools.ide.cli.CliException;
@@ -44,7 +32,18 @@
4432
import com.devonfw.tools.ide.repo.DefaultToolRepository;
4533
import com.devonfw.tools.ide.repo.ToolRepository;
4634
import com.devonfw.tools.ide.url.model.UrlMetadata;
47-
import com.devonfw.tools.ide.variable.IdeVariables;
35+
36+
import java.io.IOException;
37+
import java.net.InetAddress;
38+
import java.nio.file.Files;
39+
import java.nio.file.Path;
40+
import java.util.HashMap;
41+
import java.util.Iterator;
42+
import java.util.List;
43+
import java.util.Locale;
44+
import java.util.Map;
45+
import java.util.Objects;
46+
import java.util.function.Function;
4847

4948
/**
5049
* Abstract base implementation of {@link IdeContext}.
@@ -119,20 +118,25 @@ public abstract class AbstractIdeContext implements IdeContext {
119118

120119
private UrlMetadata urlMetadata;
121120

121+
private Path defaultExecutionDirectory;
122+
122123
/**
123124
* The constructor.
124125
*
125126
* @param minLogLevel the minimum {@link IdeLogLevel} to enable. Should be {@link IdeLogLevel#INFO} by default.
126127
* @param factory the {@link Function} to create {@link IdeSubLogger} per {@link IdeLogLevel}.
127128
* @param userDir the optional {@link Path} to current working directory.
129+
* @param toolRepository @param toolRepository the {@link ToolRepository} of the context. If it is set to {@code null}
130+
* {@link DefaultToolRepository} will be used.
128131
*/
129-
public AbstractIdeContext(IdeLogLevel minLogLevel, Function<IdeLogLevel, IdeSubLogger> factory, Path userDir) {
132+
public AbstractIdeContext(IdeLogLevel minLogLevel, Function<IdeLogLevel, IdeSubLogger> factory, Path userDir,
133+
ToolRepository toolRepository) {
130134

131135
super();
132136
this.loggerFactory = factory;
133137
this.loggers = new HashMap<>();
134138
setLogLevel(minLogLevel);
135-
this.systemInfo = new SystemInfoImpl();
139+
this.systemInfo = SystemInfoImpl.INSTANCE;
136140
this.commandletManager = new CommandletManagerImpl(this);
137141
this.fileAccess = new FileAccessImpl(this);
138142
String workspace = WORKSPACE_MAIN;
@@ -232,7 +236,13 @@ public AbstractIdeContext(IdeLogLevel minLogLevel, Function<IdeLogLevel, IdeSubL
232236
this.downloadPath = this.userHome.resolve("Downloads/ide");
233237
this.variables = createVariables();
234238
this.path = computeSystemPath();
235-
this.defaultToolRepository = new DefaultToolRepository(this);
239+
240+
if (toolRepository == null) {
241+
this.defaultToolRepository = new DefaultToolRepository(this);
242+
} else {
243+
this.defaultToolRepository = toolRepository;
244+
}
245+
236246
this.customToolRepository = CustomToolRepositoryImpl.of(this);
237247
this.workspaceMerger = new DirectoryMerger(this);
238248
}
@@ -249,7 +259,7 @@ private String getMessageIdeHomeNotFound() {
249259

250260
/**
251261
* @return the status message about the {@link #getIdeHome() IDE_HOME} detection and environment variable
252-
* initialization.
262+
* initialization.
253263
*/
254264
public String getMessageIdeHome() {
255265

@@ -277,18 +287,14 @@ public boolean isMock() {
277287

278288
private SystemPath computeSystemPath() {
279289

280-
String systemPath = System.getenv(IdeVariables.PATH.getName());
281-
return new SystemPath(systemPath, this.softwarePath, this);
290+
return new SystemPath(this);
282291
}
283292

284293
private boolean isIdeHome(Path dir) {
285294

286-
if (!Files.isRegularFile(dir.resolve("setup"))) {
287-
return false;
288-
} else if (!Files.isDirectory(dir.resolve("scripts"))) {
295+
if (!Files.isDirectory(dir.resolve("workspaces"))) {
289296
return false;
290-
} else if (dir.toString().endsWith("/scripts/src/main/resources")) {
291-
// TODO does this still make sense for our new Java based product?
297+
} else if (!Files.isDirectory(dir.resolve("settings"))) {
292298
return false;
293299
}
294300
return true;
@@ -590,6 +596,24 @@ public DirectoryMerger getWorkspaceMerger() {
590596
return this.workspaceMerger;
591597
}
592598

599+
/**
600+
* @return the {@link #defaultExecutionDirectory} the directory in which a command process is executed.
601+
*/
602+
public Path getDefaultExecutionDirectory() {
603+
604+
return this.defaultExecutionDirectory;
605+
}
606+
607+
/**
608+
* @param defaultExecutionDirectory new value of {@link #getDefaultExecutionDirectory()}.
609+
*/
610+
public void setDefaultExecutionDirectory(Path defaultExecutionDirectory) {
611+
612+
if (defaultExecutionDirectory != null) {
613+
this.defaultExecutionDirectory = defaultExecutionDirectory;
614+
}
615+
}
616+
593617
@Override
594618
public GitContext getGitContext() {
595619

@@ -599,6 +623,19 @@ public GitContext getGitContext() {
599623
@Override
600624
public ProcessContext newProcess() {
601625

626+
ProcessContext processContext = createProcessContext();
627+
if (this.defaultExecutionDirectory != null) {
628+
processContext.directory(this.defaultExecutionDirectory);
629+
}
630+
return processContext;
631+
}
632+
633+
/**
634+
* @return a new instance of {@link ProcessContext}.
635+
* @see #newProcess()
636+
*/
637+
protected ProcessContext createProcessContext() {
638+
602639
return new ProcessContextImpl(this);
603640
}
604641

@@ -680,8 +717,8 @@ public void setLogLevel(IdeLogLevel logLevel) {
680717
}
681718

682719
/**
683-
* Finds the matching {@link Commandlet} to run, applies {@link CliArguments} to its {@link Commandlet#getProperties()
684-
* properties} and will execute it.
720+
* Finds the matching {@link Commandlet} to run, applies {@link CliArguments} to its
721+
* {@link Commandlet#getProperties() properties} and will execute it.
685722
*
686723
* @param arguments the {@link CliArgument}.
687724
* @return the return code of the execution.
@@ -715,10 +752,9 @@ public int run(CliArguments arguments) {
715752

716753
/**
717754
* @param cmd the potential {@link Commandlet} to
718-
* {@link #apply(CliArguments, Commandlet, CompletionCandidateCollector) apply} and {@link Commandlet#run()
719-
* run}.
755+
* {@link #apply(CliArguments, Commandlet, CompletionCandidateCollector) apply} and {@link Commandlet#run() run}.
720756
* @return {@code true} if the given {@link Commandlet} matched and did {@link Commandlet#run() run} successfully,
721-
* {@code false} otherwise (the {@link Commandlet} did not match and we have to try a different candidate).
757+
* {@code false} otherwise (the {@link Commandlet} did not match and we have to try a different candidate).
722758
*/
723759
private boolean applyAndRun(CliArguments arguments, Commandlet cmd) {
724760

@@ -779,12 +815,12 @@ public List<CompletionCandidate> complete(CliArguments arguments, boolean includ
779815

780816
/**
781817
* @param arguments the {@link CliArguments} to apply. Will be {@link CliArguments#next() consumed} as they are
782-
* matched. Consider passing a {@link CliArguments#copy() copy} as needed.
818+
* matched. Consider passing a {@link CliArguments#copy() copy} as needed.
783819
* @param cmd the potential {@link Commandlet} to match.
784820
* @param collector the {@link CompletionCandidateCollector}.
785821
* @return {@code true} if the given {@link Commandlet} matches to the given {@link CliArgument}(s) and those have
786-
* been applied (set in the {@link Commandlet} and {@link Commandlet#validate() validated}), {@code false}
787-
* otherwise (the {@link Commandlet} did not match and we have to try a different candidate).
822+
* been applied (set in the {@link Commandlet} and {@link Commandlet#validate() validated}), {@code false} otherwise
823+
* (the {@link Commandlet} did not match and we have to try a different candidate).
788824
*/
789825
public boolean apply(CliArguments arguments, Commandlet cmd, CompletionCandidateCollector collector) {
790826

0 commit comments

Comments
 (0)