Skip to content

Commit 8bf0c15

Browse files
authored
Merge pull request #15 from Nordstrom/pr/add-target-platform
Enable clients to modify path; add unit tests
2 parents f3ef903 + 9db96ff commit 8bf0c15

File tree

3 files changed

+143
-50
lines changed

3 files changed

+143
-50
lines changed

src/main/java/com/nordstrom/common/file/PathUtils.java

Lines changed: 90 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@
2727
* <b>Example usage of {@code getNextPath}</b>
2828
* <pre>
2929
* ...
30-
*
30+
*
3131
* /*
3232
* * This example gets the next path in sequence for base name `artifact`
3333
* * and extension `txt` in the TestNG output directory.
34-
* *
35-
* * For purposes of this example, the output directory already contains
34+
* *
35+
* * For purposes of this example, the output directory already contains
3636
* * the following files: `artifact.txt`, `artifact-3.txt`
3737
* *&#47;
38-
*
38+
*
3939
* Path collectionPath = Paths.get(testContext.getOutputDirectory());
4040
* // =&gt; C:\git\my-project\test-output\Default suite
41-
*
41+
*
4242
* Path artifactPath;
4343
* try {
4444
* artifactPath = PathUtils.getNextPath(collectionPath, "artifact", "txt");
@@ -47,7 +47,7 @@
4747
* provider.getLogger().info("Unable to get output path; no artifact was captured", e);
4848
* return;
4949
* }
50-
*
50+
*
5151
* ...
5252
* </pre>
5353
*/
@@ -56,15 +56,15 @@ public final class PathUtils {
5656
private PathUtils() {
5757
throw new AssertionError("PathUtils is a static utility class that cannot be instantiated");
5858
}
59-
59+
6060
private static final String SUREFIRE_PATH = "surefire-reports";
6161
private static final String FAILSAFE_PATH = "failsafe-reports";
62-
62+
6363
/**
6464
* This enumeration contains methods to help build proxy subclass names and select reports directories.
6565
*/
6666
public enum ReportsDirectory {
67-
67+
6868
SUREFIRE_1("(Test)(.*)", SUREFIRE_PATH),
6969
SUREFIRE_2("(.*)(Test)", SUREFIRE_PATH),
7070
SUREFIRE_3("(.*)(Tests)", SUREFIRE_PATH),
@@ -73,45 +73,46 @@ public enum ReportsDirectory {
7373
FAILSAFE_2("(.*)(IT)", FAILSAFE_PATH),
7474
FAILSAFE_3("(.*)(ITCase)", FAILSAFE_PATH),
7575
ARTIFACT(".*", "artifact-capture");
76-
76+
7777
private String regex;
7878
private String folder;
79-
79+
8080
ReportsDirectory(String regex, String folder) {
8181
this.regex = regex;
8282
this.folder = folder;
8383
}
84-
84+
8585
/**
8686
* Get the regular expression that matches class names for this constant.
87-
*
87+
*
8888
* @return class-matching regular expression string
8989
*/
9090
public String getRegEx() {
9191
return regex;
9292
}
93-
93+
9494
/**
9595
* Get the name of the folder associated with this constant.
96-
*
96+
*
9797
* @return class-related folder name
9898
*/
9999
public String getFolder() {
100100
return folder;
101101
}
102-
102+
103103
/**
104104
* Get the resolved Maven-derived path associated with this constant.
105-
*
105+
*
106+
* @param subdirs optional sub-path
106107
* @return Maven folder path
107108
*/
108-
public Path getPath() {
109-
return getTargetPath().resolve(folder);
109+
public Path getPath(String... subdirs) {
110+
return getTargetPath().resolve(Paths.get(folder, subdirs));
110111
}
111-
112+
112113
/**
113114
* Get the reports directory constant for the specified test class object.
114-
*
115+
*
115116
* @param obj test class object
116117
* @return reports directory constant
117118
*/
@@ -124,31 +125,35 @@ public static ReportsDirectory fromObject(Object obj) {
124125
}
125126
throw new IllegalStateException("Someone removed the 'default' pattern from this enumeration");
126127
}
127-
128+
128129
/**
129130
* Get reports directory path for the specified test class object.
130-
*
131+
*
131132
* @param obj test class object
132133
* @return reports directory path
133134
*/
134135
public static Path getPathForObject(Object obj) {
135-
ReportsDirectory constant = fromObject(obj);
136-
return getTargetPath().resolve(constant.folder);
136+
String[] subdirs = {};
137+
if (obj instanceof PathModifier) {
138+
String message = String.format("Null path modifier returned by: %s", obj.getClass().getName());
139+
subdirs = Objects.requireNonNull(((PathModifier) obj).getSubPath(), message);
140+
}
141+
return fromObject(obj).getPath(subdirs);
137142
}
138-
143+
139144
/**
140145
* Get the path for the 'target' folder of the current project.
141-
*
146+
*
142147
* @return path for project 'target' folder
143148
*/
144149
private static Path getTargetPath() {
145-
return Paths.get(getBaseDir(), "target");
150+
return Paths.get(getBaseDir()).resolve("target");
146151
}
147152
}
148153

149154
/**
150155
* Get the next available path in sequence for the specified base name and extension in the specified folder.
151-
*
156+
*
152157
* @param targetPath path to target directory for the next available path in sequence
153158
* @param baseName base name for the path sequence
154159
* @param extension extension for the path sequence
@@ -159,7 +164,7 @@ public static Path getNextPath(Path targetPath, String baseName, String extensio
159164
Objects.requireNonNull(targetPath, "[targetPath] must be non-null");
160165
Objects.requireNonNull(baseName, "[baseName] must be non-null");
161166
Objects.requireNonNull(extension, "[extension] must be non-null");
162-
167+
163168
File targetFile = targetPath.toFile();
164169
if ( ! (targetFile.exists() && targetFile.isDirectory())) {
165170
throw new IllegalArgumentException("[targetPath] must specify an existing directory");
@@ -170,16 +175,16 @@ public static Path getNextPath(Path targetPath, String baseName, String extensio
170175
if (extension.isEmpty()) {
171176
throw new IllegalArgumentException("[extension] must specify a non-empty string");
172177
}
173-
178+
174179
Visitor visitor = new Visitor(baseName, extension);
175180
Files.walkFileTree(targetPath, EnumSet.noneOf(FileVisitOption.class), 1, visitor);
176-
181+
177182
return targetPath.resolve(visitor.getNewName());
178183
}
179184

180185
/**
181186
* Get project base directory.
182-
*
187+
*
183188
* @return project base directory
184189
*/
185190
public static String getBaseDir() {
@@ -188,7 +193,7 @@ public static String getBaseDir() {
188193
}
189194

190195
private static class Visitor implements FileVisitor<Path> {
191-
196+
192197
private String baseName;
193198
private String extension;
194199
private int base, ext;
@@ -202,7 +207,7 @@ private static class Visitor implements FileVisitor<Path> {
202207
this.ext = extension.length() + 1;
203208
this.pathMatcher = FileSystems.getDefault().getPathMatcher("regex:\\Q" + baseName + "\\E(-\\d+)?\\." + extension);
204209
}
205-
210+
206211
@Override
207212
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
208213
return FileVisitResult.CONTINUE;
@@ -228,18 +233,65 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOExce
228233
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
229234
return FileVisitResult.CONTINUE;
230235
}
231-
236+
232237
public String getNewName() {
233238
String newName;
234-
239+
235240
if (intList.isEmpty()) {
236241
newName = baseName + "." + extension;
237242
} else {
238243
Collections.sort(intList, Collections.reverseOrder());
239244
newName = baseName + "-" + intList.get(0) + "." + extension;
240245
}
241-
246+
242247
return newName;
243248
}
244249
}
250+
251+
/**
252+
* Prepend the specified string to the indicated array.
253+
*
254+
* @param prefix string to be prepended
255+
* @param strings target string array
256+
* @return target array prefixed with the specified string
257+
*/
258+
public static String[] prepend(String prefix, String... strings) {
259+
int len = strings.length;
260+
String[] temp = new String[len + 1];
261+
if (len > 0) System.arraycopy(strings, 0, temp, 1, len);
262+
temp[0] = prefix;
263+
return temp;
264+
}
265+
266+
/**
267+
* Append the specified string to the indicated array.
268+
*
269+
* @param suffix string to be appended
270+
* @param strings target string array
271+
* @return target array with the specified string appended
272+
*/
273+
public static String[] append(String suffix, String... strings) {
274+
int len = strings.length;
275+
String[] temp = new String[len + 1];
276+
if (len > 0) System.arraycopy(strings, 0, temp, 0, len);
277+
temp[len] = suffix;
278+
return temp;
279+
}
280+
281+
/**
282+
* Classes that implement this interface are called to supply additional elements for the path returned by
283+
* {@link ReportsDirectory#getPathForObject(Object)}. This enables the implementing class to partition artifacts
284+
* based on scenario-specific criteria.
285+
*/
286+
public interface PathModifier {
287+
288+
/**
289+
* Get scenario-specific path modifier for {@link ReportsDirectory#getPathForObject(Object)}.
290+
*
291+
* @return scenario-specific path modifier
292+
*/
293+
public String[] getSubPath();
294+
295+
}
296+
245297
}

src/test/java/com/nordstrom/common/file/OSInfoTest.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,16 @@ public class OSInfoTest {
1515
@Test
1616
public void testDefaultMapping() {
1717
OSInfo<OSType> osUtils = OSInfo.getDefault();
18-
OSType expected = (osName.startsWith("windows")) ? OSType.WINDOWS : OSType.UNIX;
18+
19+
OSType expected;
20+
if (osName.startsWith("windows")) {
21+
expected = OSType.WINDOWS;
22+
} else if (osName.startsWith("mac")) {
23+
expected = OSType.MACINTOSH;
24+
} else {
25+
expected = OSType.UNIX;
26+
}
27+
1928
assertEquals(osUtils.getType(), expected, "Reported OS type doesn't match expected type");
2029
}
2130

src/test/java/com/nordstrom/common/file/PathUtilsTest.java

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import org.testng.annotations.Test;
1818

1919
public class PathUtilsTest {
20-
20+
2121
@Test
2222
public void testNextPath() throws IOException {
2323
Path outputDir = getOutputPath();
@@ -29,26 +29,54 @@ public void testNextPath() throws IOException {
2929
} else {
3030
Files.createDirectories(targetPath);
3131
}
32-
32+
3333
Path path1 = PathUtils.getNextPath(targetPath, "testNextPath", "txt");
3434
assertEquals(path1.getFileName().toString(), "testNextPath.txt");
35-
35+
3636
path1.toFile().createNewFile();
37-
37+
3838
Path path2 = PathUtils.getNextPath(targetPath, "testNextPath", "txt");
3939
assertEquals(path2.getFileName().toString(), "testNextPath-1.txt");
40-
40+
4141
path2.toFile().createNewFile();
4242
targetPath.resolve("testNextPath-9.txt").toFile().createNewFile();
4343
targetPath.resolve("testNextPath-10.txt").toFile().createNewFile();
44-
44+
4545
Path path3 = PathUtils.getNextPath(targetPath, "testNextPath", "txt");
4646
assertEquals(path3.getFileName().toString(), "testNextPath-11.txt");
47-
47+
4848
Path path4 = PathUtils.getNextPath(targetPath, "test", "txt");
4949
assertEquals(path4.getFileName().toString(), "test.txt");
5050
}
5151

52+
@Test
53+
public void testPrepend() {
54+
String[] actual = PathUtils.prepend("one", "two", "three");
55+
String[] expect = {"one", "two", "three"};
56+
assertEquals(actual, expect);
57+
}
58+
59+
@Test
60+
public void testAppend() {
61+
String[] actual = PathUtils.append("three", "one", "two");
62+
String[] expect = {"one", "two", "three"};
63+
assertEquals(actual, expect);
64+
}
65+
66+
@Test
67+
public void testBaseDir() {
68+
String actual = PathUtils.getBaseDir();
69+
String expect = getBasePath().toString();
70+
assertEquals(actual, expect);
71+
}
72+
73+
@Test
74+
public void testPathForObject() {
75+
Path actual = PathUtils.ReportsDirectory.getPathForObject(this);
76+
Path expect = getBasePath().resolve("target").resolve("surefire-reports");
77+
assertEquals(actual, expect);
78+
}
79+
5280
private Path getOutputPath() {
5381
ITestResult testResult = Reporter.getCurrentTestResult();
5482
ITestContext testContext = testResult.getTestContext();
@@ -57,30 +85,34 @@ private Path getOutputPath() {
5785
return outputDir;
5886
}
5987

88+
private Path getBasePath() {
89+
return Paths.get(System.getProperty("user.dir")).toAbsolutePath();
90+
}
91+
6092
@Test(expectedExceptions = {AssertionError.class},
6193
expectedExceptionsMessageRegExp = "PathUtils is a static utility class that cannot be instantiated")
6294
public void testPrivateConstructor() throws Throwable {
63-
95+
6496
Constructor<?>[] ctors;
6597
ctors = PathUtils.class.getDeclaredConstructors();
6698
assertEquals(ctors.length, 1, "PathUtils must have exactly one constructor");
6799
assertEquals(ctors[0].getModifiers() & Modifier.PRIVATE, Modifier.PRIVATE,
68100
"PathUtils constructor must be private");
69101
assertEquals(ctors[0].getParameterTypes().length, 0, "PathUtils constructor must have no arguments");
70-
102+
71103
try {
72104
ctors[0].setAccessible(true);
73105
ctors[0].newInstance();
74106
} catch (InvocationTargetException e) {
75107
throw e.getCause();
76108
}
77109
}
78-
110+
79111
@Test(expectedExceptions = {NullPointerException.class})
80112
public void testNullPath() throws IOException {
81113
PathUtils.getNextPath(null, "test", "txt");
82114
}
83-
115+
84116
@Test(expectedExceptions = {IllegalArgumentException.class})
85117
public void testNonExistentPath() throws IOException {
86118
PathUtils.getNextPath(Paths.get("foobar"), "test", "txt");

0 commit comments

Comments
 (0)