Bug 1289006 - Add code to assert potential crashing case never happens & tests. r=grisha draft
authorMichael Comella <michael.l.comella@gmail.com>
Mon, 25 Jul 2016 14:24:03 -0700
changeset 392631 8290e515c9010bef639e92d1b0420bebe5c7d61c
parent 392630 15d0c4a3d283924627f1f97a1f99637244c49c08
child 526363 e365a3f9c07cc27ab434a6680349b4fef9b97a7c
push id24061
push usermichael.l.comella@gmail.com
push dateMon, 25 Jul 2016 21:27:56 +0000
reviewersgrisha
bugs1289006
milestone50.0a1
Bug 1289006 - Add code to assert potential crashing case never happens & tests. r=grisha I expect the crashes occurred because one of the following: * the store already existed as a file * the store was a directory that did not have the appropriate permissions In the telemetry code, none of the cases above should happen so I assert that they never do. If the crashes did occur for these reasons, the user will unfortunately continue to crash but at least we'll know where our assumptions are going wrong. I originally intended to write a regression test for listFiles returning null but it requires the application code to be modified in non-trivial ways (e.g. accessor methods we might forget to use) so I decided against it. MozReview-Commit-ID: 9V9H84ehbdO
mobile/android/base/java/org/mozilla/gecko/telemetry/stores/TelemetryJSONFilePingStore.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/telemetry/stores/TestTelemetryJSONFilePingStore.java
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/stores/TelemetryJSONFilePingStore.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/stores/TelemetryJSONFilePingStore.java
@@ -73,19 +73,30 @@ public class TelemetryJSONFilePingStore 
 
     private final File storeDir;
     private final FilenameFilter uuidFilenameFilter;
     private final FileLastModifiedComparator fileLastModifiedComparator = new FileLastModifiedComparator();
 
     @WorkerThread // Writes to disk
     public TelemetryJSONFilePingStore(final File storeDir, final String profileName) {
         super(profileName);
+        if (storeDir.exists() && !storeDir.isDirectory()) {
+            // An alternative is to create a new directory, but we wouldn't
+            // be able to access it later so it's better to throw.
+            throw new IllegalStateException("Store dir unexpectedly exists & is not a directory - cannot continue");
+        }
+
         this.storeDir = storeDir;
         this.storeDir.mkdirs();
         uuidFilenameFilter = new FilenameRegexFilter(UUIDUtil.UUID_PATTERN);
+
+        if (!this.storeDir.canRead() || !this.storeDir.canWrite() || !this.storeDir.canExecute()) {
+            throw new IllegalStateException("Cannot read, write, or execute store dir: " +
+                    this.storeDir.canRead() + " " + this.storeDir.canWrite() + " " + this.storeDir.canExecute());
+        }
     }
 
     @VisibleForTesting File getPingFile(final String docID) {
         return new File(storeDir, docID);
     }
 
     @Override
     public void storePing(final TelemetryPing ping) throws IOException {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/telemetry/stores/TestTelemetryJSONFilePingStore.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/telemetry/stores/TestTelemetryJSONFilePingStore.java
@@ -14,16 +14,17 @@ import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.testhelpers.TestRunner;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.telemetry.TelemetryPing;
 import org.mozilla.gecko.util.FileUtils;
 
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.FilenameFilter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 
 import static org.junit.Assert.*;
@@ -65,16 +66,43 @@ public class TestTelemetryJSONFilePingSt
 
     @Test
     public void testConstructorOnlyWritesToGivenDir() throws Exception {
         // Constructor is called in @Before method
         assertTrue("Store dir exists", testDir.exists());
         assertEquals("Temp dir contains one dir (the store dir)", 1, tempDir.getRoot().list().length);
     }
 
+    @Test(expected = IllegalStateException.class)
+    public void testConstructorStoreAlreadyExistsAsNonDirectory() throws Exception {
+        final File file = tempDir.newFile();
+        new TelemetryJSONFilePingStore(file, "profileName"); // expected to throw.
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testConstructorDirIsNotReadable() throws Exception {
+        final File dir = tempDir.newFolder();
+        dir.setReadable(false);
+        new TelemetryJSONFilePingStore(dir, "profileName"); // expected to throw.
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testConstructorDirIsNotWritable() throws Exception {
+        final File dir = tempDir.newFolder();
+        dir.setWritable(false);
+        new TelemetryJSONFilePingStore(dir, "profileName"); // expected to throw.
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testConstructorDirIsNotExecutable() throws Exception {
+        final File dir = tempDir.newFolder();
+        dir.setExecutable(false);
+        new TelemetryJSONFilePingStore(dir, "profileName"); // expected to throw.
+    }
+
     @Test
     public void testStorePingStoresCorrectData() throws Exception {
         assertStoreFileCount(0);
 
         final String expectedID = getDocID();
         final TelemetryPing expectedPing = new TelemetryPing("a/server/url", generateTelemetryPayload(), expectedID);
         testStore.storePing(expectedPing);