]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8630 query by filename and ext (#1555)
authorJanos Gyerik <janos.gyerik@sonarsource.com>
Tue, 24 Jan 2017 16:32:28 +0000 (17:32 +0100)
committerDuarte Meneses <duarte.meneses@sonarsource.com>
Fri, 27 Jan 2017 15:26:30 +0000 (16:26 +0100)
12 files changed:
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FilePredicates.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FileSystem.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFilePredicates.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileExtensionPredicate.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FilenamePredicate.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultFilePredicatesTest.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FileExtensionPredicateTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FilenamePredicateTest.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStore.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java [new file with mode: 0644]

index 72614539fad84ebc0e50114129158de6d689a068..1aa792889232be2f4c8e5f9c82152ddbc58ae9c9 100644 (file)
@@ -55,6 +55,24 @@ public interface FilePredicates {
    */
   FilePredicate hasRelativePath(String s);
 
+  /**
+   * Predicate that matches files by filename, in any directory.
+   * For example, the parameter <code>Foo.java</code> will match both
+   * <code>some/path/Foo.java</code> and <code>other/path/Foo.java</code>.
+   * The parameter must match exactly, no patterns are allowed,
+   * and it must not be <code>null</code>.
+   */
+  FilePredicate hasFilename(String s);
+
+  /**
+   * Predicate that matches files by extension (case insensitive).
+   * For example, the parameter <code>java</code> will match
+   * <code>some/path/Foo.java</code> and <code>other/path/Foo.JAVA</code>
+   * but not <code>some/path/Foo.js</code>.
+   * The parameter must not be <code>null</code>.
+   */
+  FilePredicate hasExtension(String s);
+
   /**
    * Predicate that gets the files which relative or absolute path matches a wildcard pattern.
    * <br>
index 3243f8dcab56d2a34fdda21ec4c97c5dc9406310..2c9adee3092cec43cedaee7b18b3ee0ec395add1 100644 (file)
@@ -165,5 +165,9 @@ public interface FileSystem {
     InputDir inputDir(String relativePath);
     
     InputModule module();
+
+    Iterable<InputFile> getFilesByName(String filename);
+
+    Iterable<InputFile> getFilesByExtension(String extension);
   }
 }
index 864fde37b8e495f1a1c8a3518a115fc07dbe7fe6..1c5033944183b32677ed8b33d85977d9821dc17b 100644 (file)
@@ -75,6 +75,15 @@ public class DefaultFilePredicates implements FilePredicates {
     return new RelativePathPredicate(s);
   }
 
+  @Override
+  public FilePredicate hasFilename(String s) {
+    return new FilenamePredicate(s);
+  }
+
+  @Override public FilePredicate hasExtension(String s) {
+    return new FileExtensionPredicate(s);
+  }
+
   @Override
   public FilePredicate matchesPathPattern(String inclusionPattern) {
     return new PathPatternPredicate(PathPattern.create(inclusionPattern));
index 7c7632df8d808574114b5445601725081d8c8dde..de2c28766de86b4d7fc4d12cd0baf527a69f66c1 100644 (file)
@@ -20,6 +20,8 @@
 package org.sonar.api.batch.fs.internal;
 
 import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
@@ -264,6 +266,8 @@ public class DefaultFileSystem implements FileSystem {
     private final Map<String, InputFile> fileMap = new HashMap<>();
     private final Map<String, InputDir> dirMap = new HashMap<>();
     private InputModule module;
+    private final SetMultimap<String, InputFile> filesByNameCache = LinkedHashMultimap.create();
+    private final SetMultimap<String, InputFile> filesByExtensionCache = LinkedHashMultimap.create();
 
     @Override
     public Iterable<InputFile> inputFiles() {
@@ -285,9 +289,20 @@ public class DefaultFileSystem implements FileSystem {
       return module;
     }
 
+    @Override
+    public Iterable<InputFile> getFilesByName(String filename) {
+      return filesByNameCache.get(filename);
+    }
+
+    @Override public Iterable<InputFile> getFilesByExtension(String extension) {
+      return filesByExtensionCache.get(extension);
+    }
+
     @Override
     protected void doAdd(InputFile inputFile) {
       fileMap.put(inputFile.relativePath(), inputFile);
+      filesByNameCache.put(FilenamePredicate.getFilename(inputFile), inputFile);
+      filesByExtensionCache.put(FileExtensionPredicate.getExtension(inputFile), inputFile);
     }
 
     @Override
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileExtensionPredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileExtensionPredicate.java
new file mode 100644 (file)
index 0000000..8eba8b0
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import java.util.Locale;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * @since 6.3
+ */
+public class FileExtensionPredicate extends AbstractFilePredicate {
+
+  private final String extension;
+
+  public FileExtensionPredicate(String extension) {
+    this.extension = lowercase(extension);
+  }
+
+  @Override
+  public boolean apply(InputFile inputFile) {
+    return extension.equals(getExtension(inputFile));
+  }
+
+  @Override
+  public Iterable<InputFile> get(FileSystem.Index index) {
+    return index.getFilesByExtension(extension);
+  }
+
+  public static String getExtension(InputFile inputFile) {
+    return getExtension(FilenamePredicate.getFilename(inputFile));
+  }
+
+  static String getExtension(String name) {
+    int index = name.lastIndexOf('.');
+    if (index < 0) {
+      return "";
+    }
+    return lowercase(name.substring(index + 1));
+  }
+
+  private static String lowercase(String extension) {
+    return extension.toLowerCase(Locale.ENGLISH);
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FilenamePredicate.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FilenamePredicate.java
new file mode 100644 (file)
index 0000000..16a6653
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * @since 6.3
+ */
+public class FilenamePredicate extends AbstractFilePredicate {
+  private final String filename;
+
+  public FilenamePredicate(String filename) {
+    this.filename = filename;
+  }
+
+  @Override
+  public boolean apply(InputFile inputFile) {
+    return filename.equals(getFilename(inputFile));
+  }
+
+  @Override
+  public Iterable<InputFile> get(FileSystem.Index index) {
+    return index.getFilesByName(filename);
+  }
+
+  public static String getFilename(InputFile inputFile) {
+    return inputFile.file().getName();
+  }
+}
index b1befd9e62b52fd2273c6ce333b3951393256d35..2d38624aa3fcc2d480870a98a3c05996ae004e20 100644 (file)
@@ -214,4 +214,14 @@ public class DefaultFilePredicatesTest {
     assertThat(predicates.or(new FilePredicate[] {predicates.all(), predicates.none()}).apply(javaFile)).isTrue();
     assertThat(predicates.or(new FilePredicate[] {predicates.none(), predicates.none()}).apply(javaFile)).isFalse();
   }
+
+  @Test
+  public void hasFilename() {
+    assertThat(predicates.hasFilename("Action.java").apply(javaFile)).isTrue();
+  }
+
+  @Test
+  public void hasExtension() {
+    assertThat(predicates.hasExtension("java").apply(javaFile)).isTrue();
+  }
 }
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FileExtensionPredicateTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FileExtensionPredicateTest.java
new file mode 100644 (file)
index 0000000..4de9de7
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.*;
+import org.sonar.api.batch.fs.InputFile;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+import static org.sonar.api.batch.fs.internal.FileExtensionPredicate.getExtension;
+
+public class FileExtensionPredicateTest {
+
+  @Test
+  public void should_match_correct_extension() throws IOException {
+    FileExtensionPredicate predicate = new FileExtensionPredicate("bat");
+    assertThat(predicate.apply(mockWithName("prog.bat"))).isTrue();
+    assertThat(predicate.apply(mockWithName("prog.bat.bat"))).isTrue();
+  }
+
+  @Test
+  public void should_not_match_incorrect_extension() throws IOException {
+    FileExtensionPredicate predicate = new FileExtensionPredicate("bat");
+    assertThat(predicate.apply(mockWithName("prog.batt"))).isFalse();
+    assertThat(predicate.apply(mockWithName("prog.abat"))).isFalse();
+    assertThat(predicate.apply(mockWithName("prog."))).isFalse();
+    assertThat(predicate.apply(mockWithName("prog.bat."))).isFalse();
+    assertThat(predicate.apply(mockWithName("prog.bat.batt"))).isFalse();
+    assertThat(predicate.apply(mockWithName("prog"))).isFalse();
+  }
+
+  @Test
+  public void should_match_correct_extension_case_insensitively() throws IOException {
+    FileExtensionPredicate predicate = new FileExtensionPredicate("jAVa");
+    assertThat(predicate.apply(mockWithName("Program.java"))).isTrue();
+    assertThat(predicate.apply(mockWithName("Program.JAVA"))).isTrue();
+    assertThat(predicate.apply(mockWithName("Program.Java"))).isTrue();
+    assertThat(predicate.apply(mockWithName("Program.JaVa"))).isTrue();
+  }
+
+  @Test
+  public void test_empty_extension() {
+    assertThat(getExtension("prog")).isEmpty();
+    assertThat(getExtension("prog.")).isEmpty();
+    assertThat(getExtension(".")).isEmpty();
+  }
+
+  private InputFile mockWithName(String filename) throws IOException {
+    InputFile inputFile = mock(InputFile.class);
+    when(inputFile.file()).thenReturn(new File("dummy parent", filename));
+    return inputFile;
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FilenamePredicateTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FilenamePredicateTest.java
new file mode 100644 (file)
index 0000000..edcaadd
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import org.junit.*;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+
+public class FilenamePredicateTest {
+  @Test
+  public void should_match_file_by_filename() throws IOException {
+    String filename = "some name";
+    InputFile inputFile = mock(InputFile.class);
+    when(inputFile.file()).thenReturn(newDummyFile(filename));
+
+    assertThat(new FilenamePredicate(filename).apply(inputFile)).isTrue();
+  }
+
+  @Test
+  public void should_not_match_file_by_different_filename() throws IOException {
+    String filename = "some name";
+    InputFile inputFile = mock(InputFile.class);
+    when(inputFile.file()).thenReturn(newDummyFile(filename + "x"));
+
+    assertThat(new FilenamePredicate(filename).apply(inputFile)).isFalse();
+  }
+
+  @Test
+  public void should_find_matching_file_in_index() throws IOException {
+    String filename = "some name";
+    InputFile inputFile = mock(InputFile.class);
+    when(inputFile.file()).thenReturn(newDummyFile(filename));
+
+    FileSystem.Index index = mock(FileSystem.Index.class);
+    when(index.getFilesByName(filename)).thenReturn(Collections.singleton(inputFile));
+
+    assertThat(new FilenamePredicate(filename).get(index)).containsOnly(inputFile);
+  }
+
+  private File newDummyFile(String filename) {
+    return new File("dummy parent", filename);
+  }
+}
index 88c41a10d080cb6e11db0845603b2a2fdc34897a..ae6c52d38c54a129caa4850d1b94bdfcc4c8106e 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.scanner.scan.filesystem;
 
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
@@ -35,6 +37,8 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile;
 
 import com.google.common.collect.Table;
 import com.google.common.collect.TreeBasedTable;
+import org.sonar.api.batch.fs.internal.FileExtensionPredicate;
+import org.sonar.api.batch.fs.internal.FilenamePredicate;
 
 /**
  * Store of all files and dirs. This cache is shared amongst all project modules. Inclusion and
@@ -47,6 +51,8 @@ public class InputComponentStore {
   private final Table<String, String, InputDir> inputDirCache = TreeBasedTable.create();
   private final Map<String, InputModule> inputModuleCache = new HashMap<>();
   private final Map<String, InputComponent> inputComponents = new HashMap<>();
+  private final SetMultimap<String, InputFile> filesByNameCache = LinkedHashMultimap.create();
+  private final SetMultimap<String, InputFile> filesByExtensionCache = LinkedHashMultimap.create();
   private InputModule root;
 
   public Collection<InputComponent> all() {
@@ -104,6 +110,8 @@ public class InputComponentStore {
     DefaultInputFile file = (DefaultInputFile) inputFile;
     inputFileCache.put(file.moduleKey(), inputFile.relativePath(), inputFile);
     inputComponents.put(inputFile.key(), inputFile);
+    filesByNameCache.put(FilenamePredicate.getFilename(inputFile), inputFile);
+    filesByExtensionCache.put(FileExtensionPredicate.getExtension(inputFile), inputFile);
     return this;
   }
 
@@ -134,4 +142,11 @@ public class InputComponentStore {
     inputModuleCache.put(inputModule.key(), inputModule);
   }
 
+  public Iterable<InputFile> getFilesByName(String filename) {
+    return filesByNameCache.get(filename);
+  }
+
+  public Iterable<InputFile> getFilesByExtension(String extension) {
+    return filesByExtensionCache.get(extension);
+  }
 }
index 9b4e5762fb117bad3aa1be6783e308fa08bfb6a8..dd518d9d152c5720680cfdada68db7c184be366a 100644 (file)
@@ -70,4 +70,13 @@ public class ModuleInputComponentStore extends DefaultFileSystem.Cache {
   public InputModule module() {
     return inputComponentStore.getModule(moduleKey);
   }
+
+  @Override
+  public Iterable<InputFile> getFilesByName(String filename) {
+    return inputComponentStore.getFilesByName(filename);
+  }
+
+  @Override public Iterable<InputFile> getFilesByExtension(String extension) {
+    return inputComponentStore.getFilesByExtension(extension);
+  }
 }
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStoreTest.java
new file mode 100644 (file)
index 0000000..fad8bae
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.scan.filesystem;
+
+import java.io.IOException;
+import org.junit.Test;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputModule;
+import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class ModuleInputComponentStoreTest {
+  @Test
+  public void should_cache_files_by_filename() throws IOException {
+    ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+
+    String filename = "some name";
+    String moduleKey = "dummy key";
+    InputFile inputFile1 = new TestInputFileBuilder(moduleKey, "some/path/" + filename).build();
+    store.doAdd(inputFile1);
+
+    InputFile inputFile2 = new TestInputFileBuilder(moduleKey, "other/path/" + filename).build();
+    store.doAdd(inputFile2);
+
+    InputFile dummyInputFile = new TestInputFileBuilder(moduleKey, "some/path/Dummy.java").build();
+    store.doAdd(dummyInputFile);
+
+    assertThat(store.getFilesByName(filename)).containsOnly(inputFile1, inputFile2);
+  }
+
+  @Test
+  public void should_cache_files_by_extension() throws IOException {
+    ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+
+    String moduleKey = "dummy key";
+    InputFile inputFile1 = new TestInputFileBuilder(moduleKey, "some/path/Program.java").build();
+    store.doAdd(inputFile1);
+
+    InputFile inputFile2 = new TestInputFileBuilder(moduleKey, "other/path/Utils.java").build();
+    store.doAdd(inputFile2);
+
+    InputFile dummyInputFile = new TestInputFileBuilder(moduleKey, "some/path/NotJava.cpp").build();
+    store.doAdd(dummyInputFile);
+
+    assertThat(store.getFilesByExtension("java")).containsOnly(inputFile1, inputFile2);
+  }
+
+  @Test
+  public void should_not_cache_duplicates() throws IOException {
+    ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+
+    String ext = "java";
+    String filename = "Program." + ext;
+    InputFile inputFile = new TestInputFileBuilder("dummy key", "some/path/" + filename).build();
+    store.doAdd(inputFile);
+    store.doAdd(inputFile);
+    store.doAdd(inputFile);
+
+    assertThat(store.getFilesByName(filename)).containsOnly(inputFile);
+    assertThat(store.getFilesByExtension(ext)).containsOnly(inputFile);
+  }
+
+  @Test
+  public void should_get_empty_iterable_on_cache_miss() {
+    ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+
+    String ext = "java";
+    String filename = "Program." + ext;
+    InputFile inputFile = new TestInputFileBuilder("dummy key", "some/path/" + filename).build();
+    store.doAdd(inputFile);
+
+    assertThat(store.getFilesByName("nonexistent")).isEmpty();
+    assertThat(store.getFilesByExtension("nonexistent")).isEmpty();
+  }
+}