27
27
* <b>Example usage of {@code getNextPath}</b>
28
28
* <pre>
29
29
* ...
30
- *
30
+ *
31
31
* /*
32
32
* * This example gets the next path in sequence for base name `artifact`
33
33
* * 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
36
36
* * the following files: `artifact.txt`, `artifact-3.txt`
37
37
* */
38
- *
38
+ *
39
39
* Path collectionPath = Paths.get(testContext.getOutputDirectory());
40
40
* // => C:\git\my-project\test-output\Default suite
41
- *
41
+ *
42
42
* Path artifactPath;
43
43
* try {
44
44
* artifactPath = PathUtils.getNextPath(collectionPath, "artifact", "txt");
47
47
* provider.getLogger().info("Unable to get output path; no artifact was captured", e);
48
48
* return;
49
49
* }
50
- *
50
+ *
51
51
* ...
52
52
* </pre>
53
53
*/
@@ -56,15 +56,15 @@ public final class PathUtils {
56
56
private PathUtils () {
57
57
throw new AssertionError ("PathUtils is a static utility class that cannot be instantiated" );
58
58
}
59
-
59
+
60
60
private static final String SUREFIRE_PATH = "surefire-reports" ;
61
61
private static final String FAILSAFE_PATH = "failsafe-reports" ;
62
-
62
+
63
63
/**
64
64
* This enumeration contains methods to help build proxy subclass names and select reports directories.
65
65
*/
66
66
public enum ReportsDirectory {
67
-
67
+
68
68
SUREFIRE_1 ("(Test)(.*)" , SUREFIRE_PATH ),
69
69
SUREFIRE_2 ("(.*)(Test)" , SUREFIRE_PATH ),
70
70
SUREFIRE_3 ("(.*)(Tests)" , SUREFIRE_PATH ),
@@ -73,45 +73,46 @@ public enum ReportsDirectory {
73
73
FAILSAFE_2 ("(.*)(IT)" , FAILSAFE_PATH ),
74
74
FAILSAFE_3 ("(.*)(ITCase)" , FAILSAFE_PATH ),
75
75
ARTIFACT (".*" , "artifact-capture" );
76
-
76
+
77
77
private String regex ;
78
78
private String folder ;
79
-
79
+
80
80
ReportsDirectory (String regex , String folder ) {
81
81
this .regex = regex ;
82
82
this .folder = folder ;
83
83
}
84
-
84
+
85
85
/**
86
86
* Get the regular expression that matches class names for this constant.
87
- *
87
+ *
88
88
* @return class-matching regular expression string
89
89
*/
90
90
public String getRegEx () {
91
91
return regex ;
92
92
}
93
-
93
+
94
94
/**
95
95
* Get the name of the folder associated with this constant.
96
- *
96
+ *
97
97
* @return class-related folder name
98
98
*/
99
99
public String getFolder () {
100
100
return folder ;
101
101
}
102
-
102
+
103
103
/**
104
104
* Get the resolved Maven-derived path associated with this constant.
105
- *
105
+ *
106
+ * @param subdirs optional sub-path
106
107
* @return Maven folder path
107
108
*/
108
- public Path getPath () {
109
- return getTargetPath ().resolve (folder );
109
+ public Path getPath (String ... subdirs ) {
110
+ return getTargetPath ().resolve (Paths . get ( folder , subdirs ) );
110
111
}
111
-
112
+
112
113
/**
113
114
* Get the reports directory constant for the specified test class object.
114
- *
115
+ *
115
116
* @param obj test class object
116
117
* @return reports directory constant
117
118
*/
@@ -124,31 +125,35 @@ public static ReportsDirectory fromObject(Object obj) {
124
125
}
125
126
throw new IllegalStateException ("Someone removed the 'default' pattern from this enumeration" );
126
127
}
127
-
128
+
128
129
/**
129
130
* Get reports directory path for the specified test class object.
130
- *
131
+ *
131
132
* @param obj test class object
132
133
* @return reports directory path
133
134
*/
134
135
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 );
137
142
}
138
-
143
+
139
144
/**
140
145
* Get the path for the 'target' folder of the current project.
141
- *
146
+ *
142
147
* @return path for project 'target' folder
143
148
*/
144
149
private static Path getTargetPath () {
145
- return Paths .get (getBaseDir (), "target" );
150
+ return Paths .get (getBaseDir ()). resolve ( "target" );
146
151
}
147
152
}
148
153
149
154
/**
150
155
* Get the next available path in sequence for the specified base name and extension in the specified folder.
151
- *
156
+ *
152
157
* @param targetPath path to target directory for the next available path in sequence
153
158
* @param baseName base name for the path sequence
154
159
* @param extension extension for the path sequence
@@ -159,7 +164,7 @@ public static Path getNextPath(Path targetPath, String baseName, String extensio
159
164
Objects .requireNonNull (targetPath , "[targetPath] must be non-null" );
160
165
Objects .requireNonNull (baseName , "[baseName] must be non-null" );
161
166
Objects .requireNonNull (extension , "[extension] must be non-null" );
162
-
167
+
163
168
File targetFile = targetPath .toFile ();
164
169
if ( ! (targetFile .exists () && targetFile .isDirectory ())) {
165
170
throw new IllegalArgumentException ("[targetPath] must specify an existing directory" );
@@ -170,16 +175,16 @@ public static Path getNextPath(Path targetPath, String baseName, String extensio
170
175
if (extension .isEmpty ()) {
171
176
throw new IllegalArgumentException ("[extension] must specify a non-empty string" );
172
177
}
173
-
178
+
174
179
Visitor visitor = new Visitor (baseName , extension );
175
180
Files .walkFileTree (targetPath , EnumSet .noneOf (FileVisitOption .class ), 1 , visitor );
176
-
181
+
177
182
return targetPath .resolve (visitor .getNewName ());
178
183
}
179
184
180
185
/**
181
186
* Get project base directory.
182
- *
187
+ *
183
188
* @return project base directory
184
189
*/
185
190
public static String getBaseDir () {
@@ -188,7 +193,7 @@ public static String getBaseDir() {
188
193
}
189
194
190
195
private static class Visitor implements FileVisitor <Path > {
191
-
196
+
192
197
private String baseName ;
193
198
private String extension ;
194
199
private int base , ext ;
@@ -202,7 +207,7 @@ private static class Visitor implements FileVisitor<Path> {
202
207
this .ext = extension .length () + 1 ;
203
208
this .pathMatcher = FileSystems .getDefault ().getPathMatcher ("regex:\\ Q" + baseName + "\\ E(-\\ d+)?\\ ." + extension );
204
209
}
205
-
210
+
206
211
@ Override
207
212
public FileVisitResult preVisitDirectory (Path dir , BasicFileAttributes attrs ) throws IOException {
208
213
return FileVisitResult .CONTINUE ;
@@ -228,18 +233,65 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOExce
228
233
public FileVisitResult postVisitDirectory (Path dir , IOException exc ) throws IOException {
229
234
return FileVisitResult .CONTINUE ;
230
235
}
231
-
236
+
232
237
public String getNewName () {
233
238
String newName ;
234
-
239
+
235
240
if (intList .isEmpty ()) {
236
241
newName = baseName + "." + extension ;
237
242
} else {
238
243
Collections .sort (intList , Collections .reverseOrder ());
239
244
newName = baseName + "-" + intList .get (0 ) + "." + extension ;
240
245
}
241
-
246
+
242
247
return newName ;
243
248
}
244
249
}
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
+
245
297
}
0 commit comments