Bug 1109361 - Handle empty files when loading ProfileInformationCache. r=mcomella
authorRichard Newman <rnewman@mozilla.com>
Tue, 23 Dec 2014 14:28:46 -0800
changeset 221123 78614247a0b38e7e55e6e725f769a59eff71dba1
parent 221122 9b7ace6537733a86b1836469f3ce403eefd1a349
child 221124 8c93e281718e2ac8bdd01ba913ac26f2a84925cb
push id10573
push userrnewman@mozilla.com
push dateTue, 23 Dec 2014 22:29:10 +0000
treeherderfx-team@6441c30a1317 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcomella
bugs1109361
milestone37.0a1
Bug 1109361 - Handle empty files when loading ProfileInformationCache. r=mcomella
mobile/android/base/background/healthreport/ProfileInformationCache.java
mobile/android/tests/background/junit3/src/healthreport/MockProfileInformationCache.java
mobile/android/tests/background/junit3/src/healthreport/TestProfileInformationCache.java
--- a/mobile/android/base/background/healthreport/ProfileInformationCache.java
+++ b/mobile/android/base/background/healthreport/ProfileInformationCache.java
@@ -65,21 +65,25 @@ public class ProfileInformationCache imp
   //
   // We also track the OS locale here for the same reason -- we need to store
   // the default (OS) value before the locale-switching code takes effect!
   private volatile String osLocale = "";
   private volatile String appLocale = "";
 
   private volatile JSONObject addons = null;
 
-  public ProfileInformationCache(String profilePath) {
-    file = new File(profilePath + File.separator + CACHE_FILE);
+  protected ProfileInformationCache(final File f) {
+    file = f;
     Logger.pii(LOG_TAG, "Using " + file.getAbsolutePath() + " for profile information cache.");
   }
 
+  public ProfileInformationCache(final String profilePath) {
+    this(new File(profilePath + File.separator + CACHE_FILE));
+  }
+
   public synchronized void beginInitialization() {
     initialized = false;
     needsWrite = true;
   }
 
   public JSONObject toJSON() {
     JSONObject object = new JSONObject();
     try {
@@ -104,16 +108,21 @@ public class ProfileInformationCache imp
    * Attempt to restore this object from a JSON blob. If there is a version mismatch, there has
    * likely been an upgrade to the cache format. The cache can be reconstructed without data loss
    * so rather than migrating, we invalidate the cache by refusing to store the given JSONObject
    * and returning false.
    *
    * @return false if there's a version mismatch or an error, true on success.
    */
   private boolean fromJSON(JSONObject object) throws JSONException {
+    if (object == null) {
+      Logger.debug(LOG_TAG, "Can't load restore PIC from null JSON object.");
+      return false;
+    }
+
     int version = object.optInt("version", 1);
     switch (version) {
     case FORMAT_VERSION:
       blocklistEnabled = object.getBoolean("blocklist");
       telemetryEnabled = object.getBoolean("telemetry");
       isAcceptLangUserSet = object.getBoolean("isAcceptLangUserSet");
       profileCreationTime = object.getLong("profileCreated");
       addons = object.getJSONObject("addons");
@@ -125,19 +134,21 @@ public class ProfileInformationCache imp
       Logger.warn(LOG_TAG, "Unable to restore from version " + version + " PIC file: expecting " + FORMAT_VERSION);
       return false;
     }
   }
 
   protected JSONObject readFromFile() throws FileNotFoundException, JSONException {
     Scanner scanner = null;
     try {
-      scanner = new Scanner(file, "UTF-8");
-      final String contents = scanner.useDelimiter("\\A").next();
-      return new JSONObject(contents);
+      scanner = new Scanner(file, "UTF-8").useDelimiter("\\A");
+      if (!scanner.hasNext()) {
+        return null;
+      }
+      return new JSONObject(scanner.next());
     } finally {
       if (scanner != null) {
         scanner.close();
       }
     }
   }
 
   protected void writeToFile(JSONObject object) throws IOException {
--- a/mobile/android/tests/background/junit3/src/healthreport/MockProfileInformationCache.java
+++ b/mobile/android/tests/background/junit3/src/healthreport/MockProfileInformationCache.java
@@ -11,16 +11,20 @@ import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.background.healthreport.ProfileInformationCache;
 
 public class MockProfileInformationCache extends ProfileInformationCache {
   public MockProfileInformationCache(String profilePath) {
     super(profilePath);
   }
 
+  public MockProfileInformationCache(File mockFile) {
+      super(mockFile);
+  }
+
   public boolean isInitialized() {
     return this.initialized;
   }
   public boolean needsWrite() {
     return this.needsWrite;
   }
   public File getFile() {
     return this.file;
--- a/mobile/android/tests/background/junit3/src/healthreport/TestProfileInformationCache.java
+++ b/mobile/android/tests/background/junit3/src/healthreport/TestProfileInformationCache.java
@@ -7,16 +7,25 @@ import java.io.File;
 import java.io.IOException;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.background.helpers.FakeProfileTestCase;
 
 public class TestProfileInformationCache extends FakeProfileTestCase {
 
+  public final void testEmptyFile() throws Exception {
+    // createTempFile creates an empty file on disk.
+    final File emptyFile = File.createTempFile("empty", "pic", this.fakeProfileDirectory);
+    final MockProfileInformationCache cache = new MockProfileInformationCache(emptyFile);
+
+    // Should not throw.
+    assertNull(cache.readJSON());
+  }
+
   public final void testInitState() throws IOException {
     MockProfileInformationCache cache = new MockProfileInformationCache(this.fakeProfileDirectory.getAbsolutePath());
     assertFalse(cache.isInitialized());
     assertFalse(cache.needsWrite());
 
     try {
       cache.isBlocklistEnabled();
       fail("Should throw fetching isBlocklistEnabled.");