33
33
import java .util .Arrays ;
34
34
import java .util .Collections ;
35
35
import java .util .Date ;
36
+ import java .util .HashSet ;
36
37
import java .util .List ;
38
+ import java .util .Objects ;
39
+ import java .util .Set ;
40
+ import java .util .stream .Stream ;
37
41
import javax .xml .parsers .DocumentBuilder ;
38
42
import javax .xml .parsers .DocumentBuilderFactory ;
39
43
44
+ import org .jetbrains .annotations .Nullable ;
40
45
import org .junit .jupiter .api .AfterEach ;
41
46
import org .junit .jupiter .api .BeforeEach ;
42
47
import org .junit .jupiter .api .Test ;
43
48
import org .junit .jupiter .params .ParameterizedTest ;
44
- import org .junit .jupiter .params .provider .ValueSource ;
49
+ import org .junit .jupiter .params .provider .Arguments ;
50
+ import org .junit .jupiter .params .provider .MethodSource ;
51
+ import org .mockito .ArgumentMatchers ;
45
52
import org .opengrok .indexer .configuration .Filter ;
46
53
import org .opengrok .indexer .configuration .IgnoredNames ;
47
54
import org .opengrok .indexer .configuration .Project ;
52
59
import org .opengrok .indexer .index .Indexer ;
53
60
import org .opengrok .indexer .search .DirectoryEntry ;
54
61
import org .opengrok .indexer .util .TestRepository ;
62
+ import org .opengrok .indexer .web .EftarFile ;
55
63
import org .opengrok .indexer .web .EftarFileReader ;
64
+ import org .opengrok .indexer .web .PathDescription ;
56
65
import org .opengrok .indexer .web .Util ;
57
66
import org .w3c .dom .Document ;
58
67
import org .w3c .dom .Element ;
65
74
import static org .mockito .ArgumentMatchers .anyString ;
66
75
import static org .mockito .Mockito .atLeast ;
67
76
import static org .mockito .Mockito .mock ;
77
+ import static org .mockito .Mockito .never ;
68
78
import static org .mockito .Mockito .when ;
69
79
import static org .mockito .Mockito .spy ;
70
80
import static org .mockito .Mockito .verify ;
@@ -116,17 +126,20 @@ static class FileEntry implements Comparable<FileEntry> {
116
126
long size ;
117
127
String readableSize ;
118
128
List <FileEntry > subdirs ;
129
+ String pathDesc ;
119
130
120
131
FileEntry () {
121
132
}
122
133
123
- private FileEntry (String name , String href , Long lastModified , long size , List <FileEntry > subdirs ) {
134
+ private FileEntry (String name , String href , Long lastModified , long size , List <FileEntry > subdirs ,
135
+ String pathDesc ) {
124
136
this .name = name ;
125
137
this .href = href ;
126
138
this .lastModified = lastModified ;
127
139
this .size = size ;
128
140
this .readableSize = Util .readableSize (size );
129
141
this .subdirs = subdirs ;
142
+ this .pathDesc = pathDesc ;
130
143
}
131
144
132
145
/**
@@ -138,7 +151,7 @@ private FileEntry(String name, String href, Long lastModified, long size, List<F
138
151
* @param subdirs list of sub entries (may be empty)
139
152
*/
140
153
FileEntry (String name , String href , long lastModified , List <FileEntry > subdirs ) {
141
- this (name , href , lastModified , DIRECTORY_INTERNAL_SIZE , subdirs );
154
+ this (name , href , lastModified , DIRECTORY_INTERNAL_SIZE , subdirs , null );
142
155
assertNotNull (subdirs );
143
156
}
144
157
@@ -151,7 +164,7 @@ private FileEntry(String name, String href, Long lastModified, long size, List<F
151
164
* @param size the desired size of the file on the disc
152
165
*/
153
166
FileEntry (String name , String href , long lastModified , int size ) {
154
- this (name , href , lastModified , size , null );
167
+ this (name , href , lastModified , size , null , null );
155
168
}
156
169
157
170
@ Override
@@ -166,6 +179,10 @@ public int compareTo(FileEntry fe) {
166
179
return ret ;
167
180
}
168
181
182
+ if (!Objects .equals (pathDesc , fe .pathDesc )) {
183
+ return -1 ;
184
+ }
185
+
169
186
if ((ret = Long .compare (lastModified , fe .lastModified )) != 0 ) {
170
187
return ret ;
171
188
}
@@ -323,20 +340,34 @@ private String getStringSize(Node item) throws NumberFormatException {
323
340
return val .getNodeValue ().trim ();
324
341
}
325
342
343
+ private @ Nullable String getStringOrNull (Node item ) throws NumberFormatException {
344
+ Node val = item .getFirstChild ();
345
+ if (val == null ) {
346
+ return null ;
347
+ }
348
+ assertEquals (Node .TEXT_NODE , val .getNodeType ());
349
+ return val .getNodeValue ().trim ();
350
+ }
351
+
326
352
/**
327
353
* Validate this file-entry in the table.
328
354
*
329
355
* @param element The <tr> element
330
356
*/
331
- private void validateEntry (Element element ) throws Exception {
357
+ private void validateEntry (Element element , boolean usePathDescriptions ) throws Exception {
332
358
FileEntry entry = new FileEntry ();
333
359
NodeList nl = element .getElementsByTagName ("td" );
334
360
int len = nl .getLength ();
335
361
// There should be 5 columns or fewer in the table.
336
362
if (len < 5 ) {
337
363
return ;
338
364
}
339
- assertEquals (7 , len , "table <td> count" );
365
+
366
+ if (usePathDescriptions ) {
367
+ assertTrue (len >= 7 , "table <td> count" );
368
+ } else {
369
+ assertEquals (7 , len , "table <td> count" );
370
+ }
340
371
341
372
// item(0) is a decoration placeholder, i.e. no content
342
373
entry .name = getFilename (nl .item (1 ));
@@ -346,6 +377,10 @@ private void validateEntry(Element element) throws Exception {
346
377
if (entry .size == INVALID_SIZE ) {
347
378
entry .readableSize = getStringSize (nl .item (4 ));
348
379
}
380
+ // item(5) and item(6) are Lines# and LOC, respectively.
381
+ if (len > 7 ) {
382
+ entry .pathDesc = getStringOrNull (nl .item (7 ));
383
+ }
349
384
350
385
// Try to look it up in the list of files.
351
386
for (FileEntry e : entries ) {
@@ -358,14 +393,23 @@ private void validateEntry(Element element) throws Exception {
358
393
throw new AssertionError ("Could not find a match for: " + entry .name );
359
394
}
360
395
396
+ private static Stream <Arguments > provideArgumentsForTestDirectoryListing () {
397
+ return Stream .of (
398
+ Arguments .of (true , true ),
399
+ Arguments .of (true , false ),
400
+ Arguments .of (false , true ),
401
+ Arguments .of (false , false )
402
+ );
403
+ }
404
+
361
405
/**
362
406
* Test directory listing.
363
407
*
364
408
* @throws java.lang.Exception if an error occurs while generating the list.
365
409
*/
366
410
@ ParameterizedTest
367
- @ ValueSource ( booleans = { true , false } )
368
- void testDirectoryListing (boolean useHistoryCache ) throws Exception {
411
+ @ MethodSource ( "provideArgumentsForTestDirectoryListing" )
412
+ void testDirectoryListing (boolean useHistoryCache , boolean usePathDescriptions ) throws Exception {
369
413
370
414
env .setUseHistoryCacheForDirectoryListing (useHistoryCache );
371
415
@@ -382,10 +426,26 @@ void testDirectoryListing(boolean useHistoryCache) throws Exception {
382
426
env , true , true ,
383
427
null , List .of (env .getPathRelativeToSourceRoot (directory )));
384
428
385
- Document document = getDocumentWithDirectoryListing ();
386
-
387
- // Construct the expected directory entries.
388
- setEntries (useHistoryCache );
429
+ Document document ;
430
+ if (usePathDescriptions ) {
431
+ File eftarFile = File .createTempFile ("paths" , ".eftar" );
432
+ Set <PathDescription > descriptions = new HashSet <>();
433
+ descriptions .add (new PathDescription ("/" + PROJECT_NAME + "/main.c" ,
434
+ "Description for main.c" ));
435
+ EftarFile ef = new EftarFile ();
436
+ ef .create (descriptions , eftarFile .getAbsolutePath ());
437
+ try (EftarFileReader eftarFileReader = new EftarFileReader (eftarFile )) {
438
+ document = getDocumentWithDirectoryListing (eftarFileReader );
439
+ // Construct the expected directory entries.
440
+ setEntries (useHistoryCache , eftarFileReader );
441
+ // Make sure there are some entries with path description.
442
+ assertTrue (entries .stream ().anyMatch (e -> e .pathDesc != null ));
443
+ }
444
+ } else {
445
+ document = getDocumentWithDirectoryListing (null );
446
+ // Construct the expected directory entries.
447
+ setEntries (useHistoryCache , null );
448
+ }
389
449
390
450
// Verify the values of directory entries.
391
451
NodeList nl = document .getElementsByTagName ("tr" );
@@ -394,11 +454,11 @@ void testDirectoryListing(boolean useHistoryCache) throws Exception {
394
454
assertEquals (entries .size () + 2 , len );
395
455
// Skip the header and parent link.
396
456
for (int i = 2 ; i < len ; ++i ) {
397
- validateEntry ((Element ) nl .item (i ));
457
+ validateEntry ((Element ) nl .item (i ), usePathDescriptions );
398
458
}
399
459
}
400
460
401
- private void setEntries (boolean useHistoryCache ) throws Exception {
461
+ private void setEntries (boolean useHistoryCache , EftarFileReader eftarFileReader ) throws Exception {
402
462
File [] files = directory .listFiles ();
403
463
assertNotNull (files );
404
464
entries = new ArrayList <>();
@@ -412,10 +472,18 @@ private void setEntries(boolean useHistoryCache) throws Exception {
412
472
// See the comment about always creating history cache in the caller.
413
473
assertTrue (historyGuru .hasHistoryCacheForFile (file ));
414
474
475
+ String pathDesc = null ;
476
+ if (eftarFileReader != null ) {
477
+ EftarFileReader .FNode parentFNode = eftarFileReader .getNode ("/" + PROJECT_NAME );
478
+ if (parentFNode != null ) {
479
+ pathDesc = eftarFileReader .getChildTag (parentFNode , file .getName ());
480
+ }
481
+ }
482
+
415
483
if (useHistoryCache ) {
416
484
if (file .isDirectory ()) {
417
485
entries .add (new FileEntry (file .getName (), file .getName () + "/" ,
418
- NO_DATE , DIRECTORY_INTERNAL_SIZE , null ));
486
+ NO_DATE , DIRECTORY_INTERNAL_SIZE , null , pathDesc ));
419
487
} else {
420
488
HistoryEntry historyEntry = historyGuru .getLastHistoryEntry (file , true , false );
421
489
assertNotNull (historyEntry );
@@ -424,7 +492,7 @@ private void setEntries(boolean useHistoryCache) throws Exception {
424
492
String dateString = dateFormatter .format (historyEntry .getDate ());
425
493
long lastModTime = dateFormatter .parse (dateString ).getTime ();
426
494
entries .add (new FileEntry (file .getName (), file .getName (),
427
- lastModTime , file .length (), null ));
495
+ lastModTime , file .length (), null , pathDesc ));
428
496
}
429
497
} else {
430
498
// The date string displayed in the UI has simple form so use the following
@@ -440,24 +508,32 @@ private void setEntries(boolean useHistoryCache) throws Exception {
440
508
441
509
if (file .isDirectory ()) {
442
510
entries .add (new FileEntry (file .getName (), file .getName () + "/" ,
443
- lastModTime , DIRECTORY_INTERNAL_SIZE , null ));
511
+ lastModTime , DIRECTORY_INTERNAL_SIZE , null , pathDesc ));
444
512
} else {
445
513
entries .add (new FileEntry (file .getName (), file .getName (),
446
- lastModTime , file .length (), null ));
514
+ lastModTime , file .length (), null , pathDesc ));
447
515
}
448
516
}
449
517
}
450
518
}
451
519
452
- private Document getDocumentWithDirectoryListing () throws Exception {
453
- StringWriter out = new StringWriter ();
520
+ private Document getDocumentWithDirectoryListing (@ Nullable EftarFileReader eftarFileReader ) throws Exception {
521
+ StringWriter outOrig = new StringWriter ();
522
+ StringWriter out = spy (outOrig );
454
523
out .append ("<?xml version=\" 1.0\" encoding=\" UTF-8\" ?>\n <start>\n " );
455
524
456
- DirectoryListing instance = new DirectoryListing ();
525
+ DirectoryListing instance ;
526
+ if (eftarFileReader != null ) {
527
+ instance = new DirectoryListing (eftarFileReader );
528
+ } else {
529
+ instance = new DirectoryListing ();
530
+ }
457
531
assertNotNull (directory .list ());
458
532
instance .listTo ("ctx" , directory , out , directory .getName (),
459
533
Arrays .asList (directory .list ()));
460
534
535
+ verify (out , never ()).write ((String ) ArgumentMatchers .isNull ());
536
+
461
537
DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance ();
462
538
assertNotNull (factory , "DocumentBuilderFactory is null" );
463
539
0 commit comments