1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.hadoop.fs.shell.find;
19 
20 import static org.junit.Assert.*;
21 import static org.mockito.Mockito.*;
22 import static org.mockito.Matchers.*;
23 
24 import java.io.IOException;
25 import java.io.PrintStream;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.LinkedList;
29 
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.fs.FileStatus;
32 import org.apache.hadoop.fs.FileSystem;
33 import org.apache.hadoop.fs.Path;
34 import org.apache.hadoop.fs.shell.PathData;
35 import org.apache.hadoop.fs.shell.find.BaseExpression;
36 import org.apache.hadoop.fs.shell.find.Expression;
37 import org.apache.hadoop.fs.shell.find.Find;
38 import org.apache.hadoop.fs.shell.find.FindOptions;
39 import org.apache.hadoop.fs.shell.find.Result;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.mockito.InOrder;
43 
44 public class TestFind {
45   private static FileSystem mockFs;
46   private static Configuration conf;
47 
48   @Before
setup()49   public void setup() throws IOException {
50     mockFs = MockFileSystem.setup();
51     conf = mockFs.getConf();
52   }
53 
54   // check follow link option is recognized
55   @Test(timeout = 1000)
processOptionsFollowLink()56   public void processOptionsFollowLink() throws IOException {
57     Find find = new Find();
58     String args = "-L path";
59     find.processOptions(getArgs(args));
60     assertTrue(find.getOptions().isFollowLink());
61     assertFalse(find.getOptions().isFollowArgLink());
62   }
63 
64   // check follow arg link option is recognized
65   @Test(timeout = 1000)
processOptionsFollowArgLink()66   public void processOptionsFollowArgLink() throws IOException {
67     Find find = new Find();
68     String args = "-H path";
69     find.processOptions(getArgs(args));
70     assertFalse(find.getOptions().isFollowLink());
71     assertTrue(find.getOptions().isFollowArgLink());
72   }
73 
74   // check follow arg link option is recognized
75   @Test(timeout = 1000)
processOptionsFollowLinkFollowArgLink()76   public void processOptionsFollowLinkFollowArgLink() throws IOException {
77     Find find = new Find();
78     String args = "-L -H path";
79     find.processOptions(getArgs(args));
80     assertTrue(find.getOptions().isFollowLink());
81 
82     // follow link option takes precedence over follow arg link
83     assertFalse(find.getOptions().isFollowArgLink());
84   }
85 
86   // check options and expressions are stripped from args leaving paths
87   @Test(timeout = 1000)
processOptionsExpression()88   public void processOptionsExpression() throws IOException {
89     Find find = new Find();
90     find.setConf(conf);
91 
92     String paths = "path1 path2 path3";
93     String args = "-L -H " + paths + " -print -name test";
94     LinkedList<String> argsList = getArgs(args);
95     find.processOptions(argsList);
96     LinkedList<String> pathList = getArgs(paths);
97     assertEquals(pathList, argsList);
98   }
99 
100   // check print is used as the default expression
101   @Test(timeout = 1000)
processOptionsNoExpression()102   public void processOptionsNoExpression() throws IOException {
103     Find find = new Find();
104     find.setConf(conf);
105     String args = "path";
106     String expected = "Print(;)";
107     find.processOptions(getArgs(args));
108     Expression expression = find.getRootExpression();
109     assertEquals(expected, expression.toString());
110   }
111 
112   // check unknown options are rejected
113   @Test(timeout = 1000)
processOptionsUnknown()114   public void processOptionsUnknown() throws IOException {
115     Find find = new Find();
116     find.setConf(conf);
117     String args = "path -unknown";
118     try {
119       find.processOptions(getArgs(args));
120       fail("Unknown expression not caught");
121     } catch (IOException e) {
122     }
123   }
124 
125   // check unknown options are rejected when mixed with known options
126   @Test(timeout = 1000)
processOptionsKnownUnknown()127   public void processOptionsKnownUnknown() throws IOException {
128     Find find = new Find();
129     find.setConf(conf);
130     String args = "path -print -unknown -print";
131     try {
132       find.processOptions(getArgs(args));
133       fail("Unknown expression not caught");
134     } catch (IOException e) {
135     }
136   }
137 
138   // check no path defaults to current working directory
139   @Test(timeout = 1000)
processOptionsNoPath()140   public void processOptionsNoPath() throws IOException {
141     Find find = new Find();
142     find.setConf(conf);
143     String args = "-print";
144 
145     LinkedList<String> argsList = getArgs(args);
146     find.processOptions(argsList);
147     assertEquals(Collections.singletonList(Path.CUR_DIR), argsList);
148   }
149 
150   // check -name is handled correctly
151   @Test(timeout = 1000)
processOptionsName()152   public void processOptionsName() throws IOException {
153     Find find = new Find();
154     find.setConf(conf);
155     String args = "path -name namemask";
156     String expected = "And(;Name(namemask;),Print(;))";
157     find.processOptions(getArgs(args));
158     Expression expression = find.getRootExpression();
159     assertEquals(expected, expression.toString());
160   }
161 
162   // check -iname is handled correctly
163   @Test(timeout = 1000)
processOptionsIname()164   public void processOptionsIname() throws IOException {
165     Find find = new Find();
166     find.setConf(conf);
167     String args = "path -iname namemask";
168     String expected = "And(;Iname-Name(namemask;),Print(;))";
169     find.processOptions(getArgs(args));
170     Expression expression = find.getRootExpression();
171     assertEquals(expected, expression.toString());
172   }
173 
174   // check -print is handled correctly
175   @Test(timeout = 1000)
processOptionsPrint()176   public void processOptionsPrint() throws IOException {
177     Find find = new Find();
178     find.setConf(conf);
179     String args = "path -print";
180     String expected = "Print(;)";
181     find.processOptions(getArgs(args));
182     Expression expression = find.getRootExpression();
183     assertEquals(expected, expression.toString());
184   }
185 
186   // check -print0 is handled correctly
187   @Test(timeout = 1000)
processOptionsPrint0()188   public void processOptionsPrint0() throws IOException {
189     Find find = new Find();
190     find.setConf(conf);
191     String args = "path -print0";
192     String expected = "Print0-Print(;)";
193     find.processOptions(getArgs(args));
194     Expression expression = find.getRootExpression();
195     assertEquals(expected, expression.toString());
196   }
197 
198   // check an implicit and is handled correctly
199   @Test(timeout = 1000)
processOptionsNoop()200   public void processOptionsNoop() throws IOException {
201     Find find = new Find();
202     find.setConf(conf);
203 
204     String args = "path -name one -name two -print";
205     String expected = "And(;And(;Name(one;),Name(two;)),Print(;))";
206     find.processOptions(getArgs(args));
207     Expression expression = find.getRootExpression();
208     assertEquals(expected, expression.toString());
209   }
210 
211   // check -a is handled correctly
212   @Test(timeout = 1000)
processOptionsA()213   public void processOptionsA() throws IOException {
214     Find find = new Find();
215     find.setConf(conf);
216 
217     String args = "path -name one -a -name two -a -print";
218     String expected = "And(;And(;Name(one;),Name(two;)),Print(;))";
219     find.processOptions(getArgs(args));
220     Expression expression = find.getRootExpression();
221     assertEquals(expected, expression.toString());
222   }
223 
224   // check -and is handled correctly
225   @Test(timeout = 1000)
processOptionsAnd()226   public void processOptionsAnd() throws IOException {
227     Find find = new Find();
228     find.setConf(conf);
229 
230     String args = "path -name one -and -name two -and -print";
231     String expected = "And(;And(;Name(one;),Name(two;)),Print(;))";
232     find.processOptions(getArgs(args));
233     Expression expression = find.getRootExpression();
234     assertEquals(expected, expression.toString());
235   }
236 
237   // check expressions are called in the correct order
238   @Test(timeout = 1000)
processArguments()239   public void processArguments() throws IOException {
240     LinkedList<PathData> items = createDirectories();
241 
242     Find find = new Find();
243     find.setConf(conf);
244     PrintStream out = mock(PrintStream.class);
245     find.getOptions().setOut(out);
246     PrintStream err = mock(PrintStream.class);
247     find.getOptions().setErr(err);
248     Expression expr = mock(Expression.class);
249     when(expr.apply((PathData) any(), anyInt())).thenReturn(Result.PASS);
250     FileStatusChecker fsCheck = mock(FileStatusChecker.class);
251     Expression test = new TestExpression(expr, fsCheck);
252     find.setRootExpression(test);
253     find.processArguments(items);
254 
255     InOrder inOrder = inOrder(expr);
256     inOrder.verify(expr).setOptions(find.getOptions());
257     inOrder.verify(expr).prepare();
258     inOrder.verify(expr).apply(item1, 0);
259     inOrder.verify(expr).apply(item1a, 1);
260     inOrder.verify(expr).apply(item1aa, 2);
261     inOrder.verify(expr).apply(item1b, 1);
262     inOrder.verify(expr).apply(item2, 0);
263     inOrder.verify(expr).apply(item3, 0);
264     inOrder.verify(expr).apply(item4, 0);
265     inOrder.verify(expr).apply(item5, 0);
266     inOrder.verify(expr).apply(item5a, 1);
267     inOrder.verify(expr).apply(item5b, 1);
268     inOrder.verify(expr).apply(item5c, 1);
269     inOrder.verify(expr).apply(item5ca, 2);
270     inOrder.verify(expr).apply(item5d, 1);
271     inOrder.verify(expr).apply(item5e, 1);
272     inOrder.verify(expr).finish();
273     verifyNoMoreInteractions(expr);
274 
275     InOrder inOrderFsCheck = inOrder(fsCheck);
276     inOrderFsCheck.verify(fsCheck).check(item1.stat);
277     inOrderFsCheck.verify(fsCheck).check(item1a.stat);
278     inOrderFsCheck.verify(fsCheck).check(item1aa.stat);
279     inOrderFsCheck.verify(fsCheck).check(item1b.stat);
280     inOrderFsCheck.verify(fsCheck).check(item2.stat);
281     inOrderFsCheck.verify(fsCheck).check(item3.stat);
282     inOrderFsCheck.verify(fsCheck).check(item4.stat);
283     inOrderFsCheck.verify(fsCheck).check(item5.stat);
284     inOrderFsCheck.verify(fsCheck).check(item5a.stat);
285     inOrderFsCheck.verify(fsCheck).check(item5b.stat);
286     inOrderFsCheck.verify(fsCheck).check(item5c.stat);
287     inOrderFsCheck.verify(fsCheck).check(item5ca.stat);
288     inOrderFsCheck.verify(fsCheck).check(item5d.stat);
289     inOrderFsCheck.verify(fsCheck).check(item5e.stat);
290     verifyNoMoreInteractions(fsCheck);
291 
292     verifyNoMoreInteractions(out);
293     verifyNoMoreInteractions(err);
294   }
295 
296   // check that directories are descended correctly when -depth is specified
297   @Test(timeout = 1000)
processArgumentsDepthFirst()298   public void processArgumentsDepthFirst() throws IOException {
299     LinkedList<PathData> items = createDirectories();
300 
301     Find find = new Find();
302     find.getOptions().setDepthFirst(true);
303     find.setConf(conf);
304     PrintStream out = mock(PrintStream.class);
305     find.getOptions().setOut(out);
306     PrintStream err = mock(PrintStream.class);
307     find.getOptions().setErr(err);
308     Expression expr = mock(Expression.class);
309     when(expr.apply((PathData) any(), anyInt())).thenReturn(Result.PASS);
310     FileStatusChecker fsCheck = mock(FileStatusChecker.class);
311     Expression test = new TestExpression(expr, fsCheck);
312     find.setRootExpression(test);
313     find.processArguments(items);
314 
315     InOrder inOrder = inOrder(expr);
316     inOrder.verify(expr).setOptions(find.getOptions());
317     inOrder.verify(expr).prepare();
318     inOrder.verify(expr).apply(item1aa, 2);
319     inOrder.verify(expr).apply(item1a, 1);
320     inOrder.verify(expr).apply(item1b, 1);
321     inOrder.verify(expr).apply(item1, 0);
322     inOrder.verify(expr).apply(item2, 0);
323     inOrder.verify(expr).apply(item3, 0);
324     inOrder.verify(expr).apply(item4, 0);
325     inOrder.verify(expr).apply(item5a, 1);
326     inOrder.verify(expr).apply(item5b, 1);
327     inOrder.verify(expr).apply(item5ca, 2);
328     inOrder.verify(expr).apply(item5c, 1);
329     inOrder.verify(expr).apply(item5d, 1);
330     inOrder.verify(expr).apply(item5e, 1);
331     inOrder.verify(expr).apply(item5, 0);
332     inOrder.verify(expr).finish();
333     verifyNoMoreInteractions(expr);
334 
335     InOrder inOrderFsCheck = inOrder(fsCheck);
336     inOrderFsCheck.verify(fsCheck).check(item1aa.stat);
337     inOrderFsCheck.verify(fsCheck).check(item1a.stat);
338     inOrderFsCheck.verify(fsCheck).check(item1b.stat);
339     inOrderFsCheck.verify(fsCheck).check(item1.stat);
340     inOrderFsCheck.verify(fsCheck).check(item2.stat);
341     inOrderFsCheck.verify(fsCheck).check(item3.stat);
342     inOrderFsCheck.verify(fsCheck).check(item4.stat);
343     inOrderFsCheck.verify(fsCheck).check(item5a.stat);
344     inOrderFsCheck.verify(fsCheck).check(item5b.stat);
345     inOrderFsCheck.verify(fsCheck).check(item5ca.stat);
346     inOrderFsCheck.verify(fsCheck).check(item5c.stat);
347     inOrderFsCheck.verify(fsCheck).check(item5d.stat);
348     inOrderFsCheck.verify(fsCheck).check(item5e.stat);
349     inOrderFsCheck.verify(fsCheck).check(item5.stat);
350     verifyNoMoreInteractions(fsCheck);
351 
352     verifyNoMoreInteractions(out);
353     verifyNoMoreInteractions(err);
354   }
355 
356   // check symlinks given as path arguments are processed correctly with the
357   // follow arg option set
358   @Test(timeout = 1000)
processArgumentsOptionFollowArg()359   public void processArgumentsOptionFollowArg() throws IOException {
360     LinkedList<PathData> items = createDirectories();
361 
362     Find find = new Find();
363     find.getOptions().setFollowArgLink(true);
364     find.setConf(conf);
365     PrintStream out = mock(PrintStream.class);
366     find.getOptions().setOut(out);
367     PrintStream err = mock(PrintStream.class);
368     find.getOptions().setErr(err);
369     Expression expr = mock(Expression.class);
370     when(expr.apply((PathData) any(), anyInt())).thenReturn(Result.PASS);
371     FileStatusChecker fsCheck = mock(FileStatusChecker.class);
372     Expression test = new TestExpression(expr, fsCheck);
373     find.setRootExpression(test);
374     find.processArguments(items);
375 
376     InOrder inOrder = inOrder(expr);
377     inOrder.verify(expr).setOptions(find.getOptions());
378     inOrder.verify(expr).prepare();
379     inOrder.verify(expr).apply(item1, 0);
380     inOrder.verify(expr).apply(item1a, 1);
381     inOrder.verify(expr).apply(item1aa, 2);
382     inOrder.verify(expr).apply(item1b, 1);
383     inOrder.verify(expr).apply(item2, 0);
384     inOrder.verify(expr).apply(item3, 0);
385     inOrder.verify(expr).apply(item4, 0);
386     inOrder.verify(expr).apply(item5, 0);
387     inOrder.verify(expr).apply(item5a, 1);
388     inOrder.verify(expr).apply(item5b, 1);
389     inOrder.verify(expr).apply(item5c, 1);
390     inOrder.verify(expr).apply(item5ca, 2);
391     inOrder.verify(expr).apply(item5d, 1);
392     inOrder.verify(expr).apply(item5e, 1);
393     inOrder.verify(expr).finish();
394     verifyNoMoreInteractions(expr);
395 
396     InOrder inOrderFsCheck = inOrder(fsCheck);
397     inOrderFsCheck.verify(fsCheck).check(item1.stat);
398     inOrderFsCheck.verify(fsCheck).check(item1a.stat);
399     inOrderFsCheck.verify(fsCheck).check(item1aa.stat);
400     inOrderFsCheck.verify(fsCheck).check(item1b.stat);
401     inOrderFsCheck.verify(fsCheck).check(item2.stat);
402     inOrderFsCheck.verify(fsCheck, times(2)).check(item3.stat);
403     inOrderFsCheck.verify(fsCheck).check(item5.stat);
404     inOrderFsCheck.verify(fsCheck).check(item5a.stat);
405     inOrderFsCheck.verify(fsCheck).check(item5b.stat);
406     inOrderFsCheck.verify(fsCheck).check(item5c.stat);
407     inOrderFsCheck.verify(fsCheck).check(item5ca.stat);
408     inOrderFsCheck.verify(fsCheck).check(item5d.stat);
409     inOrderFsCheck.verify(fsCheck).check(item5e.stat);
410     verifyNoMoreInteractions(fsCheck);
411 
412     verifyNoMoreInteractions(out);
413     verifyNoMoreInteractions(err);
414   }
415 
416   // check symlinks given as path arguments are processed correctly with the
417   // follow option
418   @Test(timeout = 1000)
processArgumentsOptionFollow()419   public void processArgumentsOptionFollow() throws IOException {
420     LinkedList<PathData> items = createDirectories();
421 
422     Find find = new Find();
423     find.getOptions().setFollowLink(true);
424     find.setConf(conf);
425     PrintStream out = mock(PrintStream.class);
426     find.getOptions().setOut(out);
427     PrintStream err = mock(PrintStream.class);
428     find.getOptions().setErr(err);
429     Expression expr = mock(Expression.class);
430     when(expr.apply((PathData) any(), anyInt())).thenReturn(Result.PASS);
431     FileStatusChecker fsCheck = mock(FileStatusChecker.class);
432     Expression test = new TestExpression(expr, fsCheck);
433     find.setRootExpression(test);
434     find.processArguments(items);
435 
436     InOrder inOrder = inOrder(expr);
437     inOrder.verify(expr).setOptions(find.getOptions());
438     inOrder.verify(expr).prepare();
439     inOrder.verify(expr).apply(item1, 0);
440     inOrder.verify(expr).apply(item1a, 1);
441     inOrder.verify(expr).apply(item1aa, 2);
442     inOrder.verify(expr).apply(item1b, 1);
443     inOrder.verify(expr).apply(item2, 0);
444     inOrder.verify(expr).apply(item3, 0);
445     inOrder.verify(expr).apply(item4, 0);
446     inOrder.verify(expr).apply(item5, 0);
447     inOrder.verify(expr).apply(item5a, 1);
448     inOrder.verify(expr).apply(item5b, 1); // triggers infinite loop message
449     inOrder.verify(expr).apply(item5c, 1);
450     inOrder.verify(expr).apply(item5ca, 2);
451     inOrder.verify(expr).apply(item5d, 1);
452     inOrder.verify(expr).apply(item5ca, 2); // following item5d symlink
453     inOrder.verify(expr).apply(item5e, 1);
454     inOrder.verify(expr).finish();
455     verifyNoMoreInteractions(expr);
456 
457     InOrder inOrderFsCheck = inOrder(fsCheck);
458     inOrderFsCheck.verify(fsCheck).check(item1.stat);
459     inOrderFsCheck.verify(fsCheck).check(item1a.stat);
460     inOrderFsCheck.verify(fsCheck).check(item1aa.stat);
461     inOrderFsCheck.verify(fsCheck).check(item1b.stat);
462     inOrderFsCheck.verify(fsCheck).check(item2.stat);
463     inOrderFsCheck.verify(fsCheck, times(2)).check(item3.stat);
464     inOrderFsCheck.verify(fsCheck).check(item5.stat);
465     inOrderFsCheck.verify(fsCheck).check(item1b.stat);
466     inOrderFsCheck.verify(fsCheck).check(item5.stat);
467     inOrderFsCheck.verify(fsCheck).check(item5c.stat);
468     inOrderFsCheck.verify(fsCheck).check(item5ca.stat);
469     inOrderFsCheck.verify(fsCheck).check(item5c.stat);
470     inOrderFsCheck.verify(fsCheck, times(2)).check(item5ca.stat);
471     verifyNoMoreInteractions(fsCheck);
472 
473     verifyNoMoreInteractions(out);
474     verify(err).println(
475         "Infinite loop ignored: " + item5b.toString() + " -> "
476             + item5.toString());
477     verifyNoMoreInteractions(err);
478   }
479 
480   // check minimum depth is handledfollowLink
481   @Test(timeout = 1000)
processArgumentsMinDepth()482   public void processArgumentsMinDepth() throws IOException {
483     LinkedList<PathData> items = createDirectories();
484 
485     Find find = new Find();
486     find.getOptions().setMinDepth(1);
487     find.setConf(conf);
488     PrintStream out = mock(PrintStream.class);
489     find.getOptions().setOut(out);
490     PrintStream err = mock(PrintStream.class);
491     find.getOptions().setErr(err);
492     Expression expr = mock(Expression.class);
493     when(expr.apply((PathData) any(), anyInt())).thenReturn(Result.PASS);
494     FileStatusChecker fsCheck = mock(FileStatusChecker.class);
495     Expression test = new TestExpression(expr, fsCheck);
496     find.setRootExpression(test);
497     find.processArguments(items);
498 
499     InOrder inOrder = inOrder(expr);
500     inOrder.verify(expr).setOptions(find.getOptions());
501     inOrder.verify(expr).prepare();
502     inOrder.verify(expr).apply(item1a, 1);
503     inOrder.verify(expr).apply(item1aa, 2);
504     inOrder.verify(expr).apply(item1b, 1);
505     inOrder.verify(expr).apply(item5a, 1);
506     inOrder.verify(expr).apply(item5b, 1);
507     inOrder.verify(expr).apply(item5c, 1);
508     inOrder.verify(expr).apply(item5ca, 2);
509     inOrder.verify(expr).apply(item5d, 1);
510     inOrder.verify(expr).apply(item5e, 1);
511     inOrder.verify(expr).finish();
512     verifyNoMoreInteractions(expr);
513 
514     InOrder inOrderFsCheck = inOrder(fsCheck);
515     inOrderFsCheck.verify(fsCheck).check(item1a.stat);
516     inOrderFsCheck.verify(fsCheck).check(item1aa.stat);
517     inOrderFsCheck.verify(fsCheck).check(item1b.stat);
518     inOrderFsCheck.verify(fsCheck).check(item5a.stat);
519     inOrderFsCheck.verify(fsCheck).check(item5b.stat);
520     inOrderFsCheck.verify(fsCheck).check(item5c.stat);
521     inOrderFsCheck.verify(fsCheck).check(item5ca.stat);
522     inOrderFsCheck.verify(fsCheck).check(item5d.stat);
523     inOrderFsCheck.verify(fsCheck).check(item5e.stat);
524     verifyNoMoreInteractions(fsCheck);
525 
526     verifyNoMoreInteractions(out);
527     verifyNoMoreInteractions(err);
528   }
529 
530   // check maximum depth is handled
531   @Test(timeout = 1000)
processArgumentsMaxDepth()532   public void processArgumentsMaxDepth() throws IOException {
533     LinkedList<PathData> items = createDirectories();
534 
535     Find find = new Find();
536     find.getOptions().setMaxDepth(1);
537     find.setConf(conf);
538     PrintStream out = mock(PrintStream.class);
539     find.getOptions().setOut(out);
540     PrintStream err = mock(PrintStream.class);
541     find.getOptions().setErr(err);
542     Expression expr = mock(Expression.class);
543     when(expr.apply((PathData) any(), anyInt())).thenReturn(Result.PASS);
544     FileStatusChecker fsCheck = mock(FileStatusChecker.class);
545     Expression test = new TestExpression(expr, fsCheck);
546     find.setRootExpression(test);
547     find.processArguments(items);
548 
549     InOrder inOrder = inOrder(expr);
550     inOrder.verify(expr).setOptions(find.getOptions());
551     inOrder.verify(expr).prepare();
552     inOrder.verify(expr).apply(item1, 0);
553     inOrder.verify(expr).apply(item1a, 1);
554     inOrder.verify(expr).apply(item1b, 1);
555     inOrder.verify(expr).apply(item2, 0);
556     inOrder.verify(expr).apply(item3, 0);
557     inOrder.verify(expr).apply(item4, 0);
558     inOrder.verify(expr).apply(item5, 0);
559     inOrder.verify(expr).apply(item5a, 1);
560     inOrder.verify(expr).apply(item5b, 1);
561     inOrder.verify(expr).apply(item5c, 1);
562     inOrder.verify(expr).apply(item5d, 1);
563     inOrder.verify(expr).apply(item5e, 1);
564     inOrder.verify(expr).finish();
565     verifyNoMoreInteractions(expr);
566 
567     InOrder inOrderFsCheck = inOrder(fsCheck);
568     inOrderFsCheck.verify(fsCheck).check(item1.stat);
569     inOrderFsCheck.verify(fsCheck).check(item1a.stat);
570     inOrderFsCheck.verify(fsCheck).check(item1b.stat);
571     inOrderFsCheck.verify(fsCheck).check(item2.stat);
572     inOrderFsCheck.verify(fsCheck).check(item3.stat);
573     inOrderFsCheck.verify(fsCheck).check(item4.stat);
574     inOrderFsCheck.verify(fsCheck).check(item5.stat);
575     inOrderFsCheck.verify(fsCheck).check(item5a.stat);
576     inOrderFsCheck.verify(fsCheck).check(item5b.stat);
577     inOrderFsCheck.verify(fsCheck).check(item5c.stat);
578     inOrderFsCheck.verify(fsCheck).check(item5d.stat);
579     inOrderFsCheck.verify(fsCheck).check(item5e.stat);
580     verifyNoMoreInteractions(fsCheck);
581 
582     verifyNoMoreInteractions(out);
583     verifyNoMoreInteractions(err);
584   }
585 
586   // check min depth is handled when -depth is specified
587   @Test(timeout = 1000)
processArgumentsDepthFirstMinDepth()588   public void processArgumentsDepthFirstMinDepth() throws IOException {
589     LinkedList<PathData> items = createDirectories();
590 
591     Find find = new Find();
592     find.getOptions().setDepthFirst(true);
593     find.getOptions().setMinDepth(1);
594     find.setConf(conf);
595     PrintStream out = mock(PrintStream.class);
596     find.getOptions().setOut(out);
597     PrintStream err = mock(PrintStream.class);
598     find.getOptions().setErr(err);
599     Expression expr = mock(Expression.class);
600     when(expr.apply((PathData) any(), anyInt())).thenReturn(Result.PASS);
601     FileStatusChecker fsCheck = mock(FileStatusChecker.class);
602     Expression test = new TestExpression(expr, fsCheck);
603     find.setRootExpression(test);
604     find.processArguments(items);
605 
606     InOrder inOrder = inOrder(expr);
607     inOrder.verify(expr).setOptions(find.getOptions());
608     inOrder.verify(expr).prepare();
609     inOrder.verify(expr).apply(item1aa, 2);
610     inOrder.verify(expr).apply(item1a, 1);
611     inOrder.verify(expr).apply(item1b, 1);
612     inOrder.verify(expr).apply(item5a, 1);
613     inOrder.verify(expr).apply(item5b, 1);
614     inOrder.verify(expr).apply(item5ca, 2);
615     inOrder.verify(expr).apply(item5c, 1);
616     inOrder.verify(expr).apply(item5d, 1);
617     inOrder.verify(expr).apply(item5e, 1);
618     inOrder.verify(expr).finish();
619     verifyNoMoreInteractions(expr);
620 
621     InOrder inOrderFsCheck = inOrder(fsCheck);
622     inOrderFsCheck.verify(fsCheck).check(item1aa.stat);
623     inOrderFsCheck.verify(fsCheck).check(item1a.stat);
624     inOrderFsCheck.verify(fsCheck).check(item1b.stat);
625     inOrderFsCheck.verify(fsCheck).check(item5a.stat);
626     inOrderFsCheck.verify(fsCheck).check(item5b.stat);
627     inOrderFsCheck.verify(fsCheck).check(item5ca.stat);
628     inOrderFsCheck.verify(fsCheck).check(item5c.stat);
629     inOrderFsCheck.verify(fsCheck).check(item5d.stat);
630     inOrderFsCheck.verify(fsCheck).check(item5e.stat);
631     verifyNoMoreInteractions(fsCheck);
632 
633     verifyNoMoreInteractions(out);
634     verifyNoMoreInteractions(err);
635   }
636 
637   // check max depth is handled when -depth is specified
638   @Test(timeout = 1000)
processArgumentsDepthFirstMaxDepth()639   public void processArgumentsDepthFirstMaxDepth() throws IOException {
640     LinkedList<PathData> items = createDirectories();
641 
642     Find find = new Find();
643     find.getOptions().setDepthFirst(true);
644     find.getOptions().setMaxDepth(1);
645     find.setConf(conf);
646     PrintStream out = mock(PrintStream.class);
647     find.getOptions().setOut(out);
648     PrintStream err = mock(PrintStream.class);
649     find.getOptions().setErr(err);
650     Expression expr = mock(Expression.class);
651     when(expr.apply((PathData) any(), anyInt())).thenReturn(Result.PASS);
652     FileStatusChecker fsCheck = mock(FileStatusChecker.class);
653     Expression test = new TestExpression(expr, fsCheck);
654     find.setRootExpression(test);
655     find.processArguments(items);
656 
657     InOrder inOrder = inOrder(expr);
658     inOrder.verify(expr).setOptions(find.getOptions());
659     inOrder.verify(expr).prepare();
660     inOrder.verify(expr).apply(item1a, 1);
661     inOrder.verify(expr).apply(item1b, 1);
662     inOrder.verify(expr).apply(item1, 0);
663     inOrder.verify(expr).apply(item2, 0);
664     inOrder.verify(expr).apply(item3, 0);
665     inOrder.verify(expr).apply(item4, 0);
666     inOrder.verify(expr).apply(item5a, 1);
667     inOrder.verify(expr).apply(item5b, 1);
668     inOrder.verify(expr).apply(item5c, 1);
669     inOrder.verify(expr).apply(item5d, 1);
670     inOrder.verify(expr).apply(item5e, 1);
671     inOrder.verify(expr).apply(item5, 0);
672     inOrder.verify(expr).finish();
673     verifyNoMoreInteractions(expr);
674 
675     InOrder inOrderFsCheck = inOrder(fsCheck);
676     inOrderFsCheck.verify(fsCheck).check(item1a.stat);
677     inOrderFsCheck.verify(fsCheck).check(item1b.stat);
678     inOrderFsCheck.verify(fsCheck).check(item1.stat);
679     inOrderFsCheck.verify(fsCheck).check(item2.stat);
680     inOrderFsCheck.verify(fsCheck).check(item3.stat);
681     inOrderFsCheck.verify(fsCheck).check(item4.stat);
682     inOrderFsCheck.verify(fsCheck).check(item5a.stat);
683     inOrderFsCheck.verify(fsCheck).check(item5b.stat);
684     inOrderFsCheck.verify(fsCheck).check(item5c.stat);
685     inOrderFsCheck.verify(fsCheck).check(item5d.stat);
686     inOrderFsCheck.verify(fsCheck).check(item5e.stat);
687     inOrderFsCheck.verify(fsCheck).check(item5.stat);
688     verifyNoMoreInteractions(fsCheck);
689 
690     verifyNoMoreInteractions(out);
691     verifyNoMoreInteractions(err);
692   }
693 
694   // check expressions are called in the correct order
695   @Test(timeout = 1000)
processArgumentsNoDescend()696   public void processArgumentsNoDescend() throws IOException {
697     LinkedList<PathData> items = createDirectories();
698 
699     Find find = new Find();
700     find.setConf(conf);
701     PrintStream out = mock(PrintStream.class);
702     find.getOptions().setOut(out);
703     PrintStream err = mock(PrintStream.class);
704     find.getOptions().setErr(err);
705     Expression expr = mock(Expression.class);
706     when(expr.apply((PathData) any(), anyInt())).thenReturn(Result.PASS);
707     when(expr.apply(eq(item1a), anyInt())).thenReturn(Result.STOP);
708     FileStatusChecker fsCheck = mock(FileStatusChecker.class);
709     Expression test = new TestExpression(expr, fsCheck);
710     find.setRootExpression(test);
711     find.processArguments(items);
712 
713     InOrder inOrder = inOrder(expr);
714     inOrder.verify(expr).setOptions(find.getOptions());
715     inOrder.verify(expr).prepare();
716     inOrder.verify(expr).apply(item1, 0);
717     inOrder.verify(expr).apply(item1a, 1);
718     inOrder.verify(expr).apply(item1b, 1);
719     inOrder.verify(expr).apply(item2, 0);
720     inOrder.verify(expr).apply(item3, 0);
721     inOrder.verify(expr).apply(item4, 0);
722     inOrder.verify(expr).apply(item5, 0);
723     inOrder.verify(expr).apply(item5a, 1);
724     inOrder.verify(expr).apply(item5b, 1);
725     inOrder.verify(expr).apply(item5c, 1);
726     inOrder.verify(expr).apply(item5ca, 2);
727     inOrder.verify(expr).apply(item5d, 1);
728     inOrder.verify(expr).apply(item5e, 1);
729     inOrder.verify(expr).finish();
730     verifyNoMoreInteractions(expr);
731 
732     InOrder inOrderFsCheck = inOrder(fsCheck);
733     inOrderFsCheck.verify(fsCheck).check(item1.stat);
734     inOrderFsCheck.verify(fsCheck).check(item1a.stat);
735     inOrderFsCheck.verify(fsCheck).check(item1b.stat);
736     inOrderFsCheck.verify(fsCheck).check(item2.stat);
737     inOrderFsCheck.verify(fsCheck).check(item3.stat);
738     inOrderFsCheck.verify(fsCheck).check(item4.stat);
739     inOrderFsCheck.verify(fsCheck).check(item5.stat);
740     inOrderFsCheck.verify(fsCheck).check(item5a.stat);
741     inOrderFsCheck.verify(fsCheck).check(item5b.stat);
742     inOrderFsCheck.verify(fsCheck).check(item5c.stat);
743     inOrderFsCheck.verify(fsCheck).check(item5ca.stat);
744     inOrderFsCheck.verify(fsCheck).check(item5d.stat);
745     inOrderFsCheck.verify(fsCheck).check(item5e.stat);
746     verifyNoMoreInteractions(fsCheck);
747 
748     verifyNoMoreInteractions(out);
749     verifyNoMoreInteractions(err);
750   }
751 
752   private interface FileStatusChecker {
check(FileStatus fileStatus)753     public void check(FileStatus fileStatus);
754   }
755 
756   private class TestExpression extends BaseExpression implements Expression {
757     private Expression expr;
758     private FileStatusChecker checker;
TestExpression(Expression expr, FileStatusChecker checker)759     public TestExpression(Expression expr, FileStatusChecker checker) {
760       this.expr = expr;
761       this.checker = checker;
762     }
763     @Override
apply(PathData item, int depth)764     public Result apply(PathData item, int depth) throws IOException {
765       FileStatus fileStatus = getFileStatus(item, depth);
766       checker.check(fileStatus);
767       return expr.apply(item, depth);
768     }
769     @Override
setOptions(FindOptions options)770     public void setOptions(FindOptions options) throws IOException {
771       super.setOptions(options);
772       expr.setOptions(options);
773     }
774     @Override
prepare()775     public void prepare() throws IOException {
776       expr.prepare();
777     }
778     @Override
finish()779     public void finish() throws IOException {
780       expr.finish();
781     }
782   }
783 
784   // creates a directory structure for traversal
785   // item1 (directory)
786   // \- item1a (directory)
787   //    \- item1aa (file)
788   // \- item1b (file)
789   // item2 (directory)
790   // item3 (file)
791   // item4 (link) -> item3
792   // item5 (directory)
793   // \- item5a (link) -> item1b
794   // \- item5b (link) -> item5 (infinite loop)
795   // \- item5c (directory)
796   //    \- item5ca (file)
797   // \- item5d (link) -> item5c
798   // \- item5e (link) -> item5c/item5ca
799   private PathData item1 = null;
800   private PathData item1a = null;
801   private PathData item1aa = null;
802   private PathData item1b = null;
803   private PathData item2 = null;
804   private PathData item3 = null;
805   private PathData item4 = null;
806   private PathData item5 = null;
807   private PathData item5a = null;
808   private PathData item5b = null;
809   private PathData item5c = null;
810   private PathData item5ca = null;
811   private PathData item5d = null;
812   private PathData item5e = null;
813 
createDirectories()814   private LinkedList<PathData> createDirectories() throws IOException {
815     item1 = createPathData("item1");
816     item1a = createPathData("item1/item1a");
817     item1aa = createPathData("item1/item1a/item1aa");
818     item1b = createPathData("item1/item1b");
819     item2 = createPathData("item2");
820     item3 = createPathData("item3");
821     item4 = createPathData("item4");
822     item5 = createPathData("item5");
823     item5a = createPathData("item5/item5a");
824     item5b = createPathData("item5/item5b");
825     item5c = createPathData("item5/item5c");
826     item5ca = createPathData("item5/item5c/item5ca");
827     item5d = createPathData("item5/item5d");
828     item5e = createPathData("item5/item5e");
829 
830     LinkedList<PathData> args = new LinkedList<PathData>();
831 
832     when(item1.stat.isDirectory()).thenReturn(true);
833     when(item1a.stat.isDirectory()).thenReturn(true);
834     when(item1aa.stat.isDirectory()).thenReturn(false);
835     when(item1b.stat.isDirectory()).thenReturn(false);
836     when(item2.stat.isDirectory()).thenReturn(true);
837     when(item3.stat.isDirectory()).thenReturn(false);
838     when(item4.stat.isDirectory()).thenReturn(false);
839     when(item5.stat.isDirectory()).thenReturn(true);
840     when(item5a.stat.isDirectory()).thenReturn(false);
841     when(item5b.stat.isDirectory()).thenReturn(false);
842     when(item5c.stat.isDirectory()).thenReturn(true);
843     when(item5ca.stat.isDirectory()).thenReturn(false);
844     when(item5d.stat.isDirectory()).thenReturn(false);
845     when(item5e.stat.isDirectory()).thenReturn(false);
846 
847     when(mockFs.listStatus(eq(item1.path))).thenReturn(
848         new FileStatus[] { item1a.stat, item1b.stat });
849     when(mockFs.listStatus(eq(item1a.path))).thenReturn(
850         new FileStatus[] { item1aa.stat });
851     when(mockFs.listStatus(eq(item2.path))).thenReturn(new FileStatus[0]);
852     when(mockFs.listStatus(eq(item5.path))).thenReturn(
853         new FileStatus[] { item5a.stat, item5b.stat, item5c.stat, item5d.stat,
854             item5e.stat });
855     when(mockFs.listStatus(eq(item5c.path))).thenReturn(
856         new FileStatus[] { item5ca.stat });
857 
858     when(item1.stat.isSymlink()).thenReturn(false);
859     when(item1a.stat.isSymlink()).thenReturn(false);
860     when(item1aa.stat.isSymlink()).thenReturn(false);
861     when(item1b.stat.isSymlink()).thenReturn(false);
862     when(item2.stat.isSymlink()).thenReturn(false);
863     when(item3.stat.isSymlink()).thenReturn(false);
864     when(item4.stat.isSymlink()).thenReturn(true);
865     when(item5.stat.isSymlink()).thenReturn(false);
866     when(item5a.stat.isSymlink()).thenReturn(true);
867     when(item5b.stat.isSymlink()).thenReturn(true);
868     when(item5d.stat.isSymlink()).thenReturn(true);
869     when(item5e.stat.isSymlink()).thenReturn(true);
870 
871     when(item4.stat.getSymlink()).thenReturn(item3.path);
872     when(item5a.stat.getSymlink()).thenReturn(item1b.path);
873     when(item5b.stat.getSymlink()).thenReturn(item5.path);
874     when(item5d.stat.getSymlink()).thenReturn(item5c.path);
875     when(item5e.stat.getSymlink()).thenReturn(item5ca.path);
876 
877     args.add(item1);
878     args.add(item2);
879     args.add(item3);
880     args.add(item4);
881     args.add(item5);
882 
883     return args;
884   }
885 
createPathData(String name)886   private PathData createPathData(String name) throws IOException {
887     Path path = new Path(name);
888     FileStatus fstat = mock(FileStatus.class);
889     when(fstat.getPath()).thenReturn(path);
890     when(fstat.toString()).thenReturn("fileStatus:" + name);
891 
892     when(mockFs.getFileStatus(eq(path))).thenReturn(fstat);
893     PathData item = new PathData(path.toString(), conf);
894     return item;
895   }
896 
getArgs(String cmd)897   private LinkedList<String> getArgs(String cmd) {
898     return new LinkedList<String>(Arrays.asList(cmd.split(" ")));
899   }
900 }
901