Home | History | Annotate | Download | only in backup
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.app.backup;
     18 
     19 import android.app.backup.BackupDataInput;
     20 import android.app.backup.BackupDataOutput;
     21 import android.content.res.AssetFileDescriptor;
     22 import android.content.res.AssetManager;
     23 import android.os.Bundle;
     24 import android.os.Environment;
     25 import android.os.ParcelFileDescriptor;
     26 import android.test.AndroidTestCase;
     27 import android.test.InstrumentationTestCase;
     28 import android.util.Base64;
     29 import android.util.Log;
     30 import org.json.JSONObject;
     31 
     32 import java.io.BufferedReader;
     33 import java.io.File;
     34 import java.io.FileInputStream;
     35 import java.io.FileNotFoundException;
     36 import java.io.FileOutputStream;
     37 import java.io.FileReader;
     38 import java.io.IOException;
     39 import java.io.InputStream;
     40 import java.io.InputStreamReader;
     41 import java.lang.Exception;
     42 import java.nio.ByteBuffer;
     43 
     44 public class BackupDataTest extends AndroidTestCase {
     45     private static final String KEY1 = "key1";
     46     private static final String KEY2 = "key2a";
     47     private static final String KEY3 = "key3bc";
     48     private static final String KEY4 = "key4dad";  // variable key lengths to test padding
     49     private static final String[] KEYS = {KEY1, KEY2, KEY3, KEY4};
     50 
     51     private static final String DATA1 = "abcdef";
     52     private static final String DATA2 = "abcdefg";
     53     private static final String DATA3 = "abcdefgh";
     54     private static final String DATA4 = "abcdeffhi"; //variable data lengths to test padding
     55     private static final String[] DATA = {DATA1, DATA2, DATA3, DATA4};
     56     private static final String TAG = "BackupDataTest";
     57 
     58     private File mFile;
     59     private ParcelFileDescriptor mDataFile;
     60     private File mDirectory;
     61     private Bundle mStatusBundle;
     62     private AssetManager mAssets;
     63 
     64     @Override
     65     protected void setUp() throws Exception {
     66         super.setUp();
     67         mDirectory = new File(Environment.getExternalStorageDirectory(), "test_data");
     68         mDirectory.mkdirs();
     69         mAssets = mContext.getAssets();
     70     }
     71 
     72     @Override
     73     protected void tearDown() throws Exception {
     74         super.tearDown();
     75         if (mDataFile != null) {
     76             mDataFile.close();
     77         }
     78     }
     79 
     80     public void testSingle() throws IOException {
     81         mFile = new File(mDirectory, "backup_mixed_sinlge.dat");
     82         openForWriting();
     83         BackupDataOutput bdo = new BackupDataOutput(mDataFile.getFileDescriptor());
     84 
     85         writeEntity(bdo, KEY1, DATA1.getBytes());
     86 
     87         mDataFile.close();
     88         openForReading();
     89 
     90         BackupDataInput bdi = new BackupDataInput(mDataFile.getFileDescriptor());
     91         int count = 0;
     92         while (bdi.readNextHeader()) {
     93             readAndVerifyEntity(bdi, KEY1, DATA1.getBytes());
     94             count++;
     95         }
     96         assertEquals("only one entity in this stream", 1, count);
     97     }
     98 
     99     public void testMultiple() throws IOException {
    100         mFile = new File(mDirectory, "backup_multiple_test.dat");
    101         openForWriting();
    102         BackupDataOutput bdo = new BackupDataOutput(mDataFile.getFileDescriptor());
    103 
    104         for(int i = 0; i < KEYS.length; i++) {
    105             writeEntity(bdo, KEYS[i], DATA[i].getBytes());
    106         }
    107 
    108         mDataFile.close();
    109         openForReading();
    110 
    111         BackupDataInput bdi = new BackupDataInput(mDataFile.getFileDescriptor());
    112         int count = 0;
    113         while (bdi.readNextHeader()) {
    114             readAndVerifyEntity(bdi, KEYS[count], DATA[count].getBytes());
    115             count++;
    116         }
    117         assertEquals("four entities in this stream", KEYS.length, count);
    118     }
    119 
    120     public void testDelete() throws IOException {
    121         mFile = new File(mDirectory, "backup_delete_test.dat");
    122         openForWriting();
    123         BackupDataOutput bdo = new BackupDataOutput(mDataFile.getFileDescriptor());
    124 
    125         for(int i = 0; i < KEYS.length; i++) {
    126             deleteEntity(bdo, KEYS[i]);
    127         }
    128 
    129         mDataFile.close();
    130         openForReading();
    131 
    132         BackupDataInput bdi = new BackupDataInput(mDataFile.getFileDescriptor());
    133         int count = 0;
    134         while (bdi.readNextHeader()) {
    135             readAndVerifyDeletedEntity(bdi, KEYS[count]);
    136             count++;
    137         }
    138         assertEquals("four deletes in this stream", KEYS.length, count);
    139     }
    140 
    141     public void testMixed() throws IOException {
    142         mFile = new File(mDirectory, "backup_mixed_test.dat");
    143         openForWriting();
    144 
    145         BackupDataOutput bdo = new BackupDataOutput(mDataFile.getFileDescriptor());
    146 
    147         int i = 0;
    148         deleteEntity(bdo, KEYS[i]); i++;
    149         writeEntity(bdo, KEYS[i], DATA[i].getBytes()); i++;
    150         writeEntity(bdo, KEYS[i], DATA[i].getBytes()); i++;
    151         deleteEntity(bdo, KEYS[i]); i++;
    152 
    153         mDataFile.close();
    154         openForReading();
    155 
    156         BackupDataInput bdi = new BackupDataInput(mDataFile.getFileDescriptor());
    157         int out = 0;
    158         assertTrue(bdi.readNextHeader());
    159         readAndVerifyDeletedEntity(bdi, KEYS[out]); out++;
    160         assertTrue(bdi.readNextHeader());
    161         readAndVerifyEntity(bdi, KEYS[out], DATA[out].getBytes()); out++;
    162         assertTrue(bdi.readNextHeader());
    163         readAndVerifyEntity(bdi, KEYS[out], DATA[out].getBytes()); out++;
    164         assertTrue(bdi.readNextHeader());
    165         readAndVerifyDeletedEntity(bdi, KEYS[out]); out++;
    166         assertFalse("four items in this stream",
    167                 bdi.readNextHeader());
    168     }
    169 
    170     public void testReadMockData() throws IOException {
    171         copyAssetToFile("backup_mock.dat", "backup_read_mock_test.dat");
    172 
    173         openForReading();
    174         BackupDataInput bdi = new BackupDataInput(mDataFile.getFileDescriptor());
    175         BufferedReader truth = new BufferedReader(new InputStreamReader(
    176                 mAssets.openFd("backup_mock.gld").createInputStream()));
    177         while( bdi.readNextHeader()) {
    178             String[] expected = truth.readLine().split(":");
    179             byte[] expectedBytes = null;
    180             if (expected.length > 1) {
    181                 expectedBytes = Base64.decode(expected[1], Base64.DEFAULT);
    182             }
    183             String key = bdi.getKey();
    184             int dataSize = bdi.getDataSize();
    185 
    186             assertEquals("wrong key", expected[0], key);
    187             assertEquals("wrong length for key " + key,
    188                     (expectedBytes == null ? -1: expectedBytes.length), dataSize);
    189             if (dataSize != -1) {
    190                 byte[] buffer = new byte[dataSize];
    191                 bdi.readEntityData(buffer, 0, dataSize);
    192                 assertEquals("wrong data for key " + key, expected[1],
    193                         Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP));
    194             }
    195         }
    196         assertNull("there are unused entries in the golden file", truth.readLine());
    197     }
    198 
    199     public void testReadRealData() throws IOException {
    200         copyAssetToFile("backup_real.dat", "backup_read_real_test.dat");
    201 
    202         openForReading();
    203         BackupDataInput bdi = new BackupDataInput(mDataFile.getFileDescriptor());
    204         BufferedReader truth = new BufferedReader(new InputStreamReader(
    205                 mAssets.openFd("backup_real.gld").createInputStream()));
    206 
    207         while(bdi.readNextHeader()) {
    208             String[] expected = truth.readLine().split(":");
    209             byte[] expectedBytes = null;
    210             if (expected.length > 1) {
    211                 expectedBytes = Base64.decode(expected[1], Base64.DEFAULT);
    212             }
    213             String key = bdi.getKey();
    214             int dataSize = bdi.getDataSize();
    215 
    216             assertEquals("wrong key", expected[0], key);
    217             assertEquals("wrong length for key " + key,
    218                     (expectedBytes == null ? -1: expectedBytes.length), dataSize);
    219             if (dataSize != -1) {
    220                 byte[] buffer = new byte[dataSize];
    221                 bdi.readEntityData(buffer, 0, dataSize);
    222                 assertEquals("wrong data for key " + key, expected[1],
    223                         Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP));
    224             }
    225         }
    226         assertNull("there are unused entries in the golden file", truth.readLine());
    227     }
    228 
    229     private void copyAssetToFile(String source, String destination) throws IOException {
    230         mFile = new File(mDirectory, destination);
    231         openForWriting();
    232         FileInputStream fileInputStream = mAssets.openFd(source).createInputStream();
    233         FileOutputStream fileOutputStream = new FileOutputStream(mDataFile.getFileDescriptor());
    234         byte[] copybuffer = new byte[1024];
    235         int numBytes = fileInputStream.read(copybuffer);
    236         fileOutputStream.write(copybuffer, 0, numBytes);
    237         fileOutputStream.close();
    238     }
    239 
    240     private void openForWriting() throws FileNotFoundException {
    241         mDataFile = ParcelFileDescriptor.open(mFile,
    242                 ParcelFileDescriptor.MODE_WRITE_ONLY |
    243                         ParcelFileDescriptor.MODE_CREATE |
    244                         ParcelFileDescriptor.MODE_TRUNCATE);  // Make an empty file if necessary
    245     }
    246 
    247     private void openForReading() throws FileNotFoundException {
    248         mDataFile = ParcelFileDescriptor.open(mFile,
    249                 ParcelFileDescriptor.MODE_READ_ONLY |
    250                         ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
    251     }
    252 
    253     private void writeEntity(BackupDataOutput bdo, String key, byte[] data) throws IOException {
    254         int status = bdo.writeEntityHeader(key, data.length);
    255         // documentation says "number of bytes written" but that's not what we get:
    256         assertEquals(0, status);
    257 
    258         status = bdo.writeEntityData(data, data.length);
    259         // documentation says "number of bytes written" but that's not what we get:
    260         assertEquals(0, status);
    261     }
    262 
    263     private void deleteEntity(BackupDataOutput bdo, String key) throws IOException {
    264         int status = bdo.writeEntityHeader(key, -1);
    265         // documentation says "number of bytes written" but that's not what we get:
    266         assertEquals(0, status);
    267     }
    268 
    269     private void readAndVerifyEntity(BackupDataInput bdi, String expectedKey, byte[] expectedData)
    270             throws IOException {
    271         assertEquals("Key mismatch",
    272                 expectedKey, bdi.getKey());
    273         assertEquals("data size mismatch",
    274                 expectedData.length, bdi.getDataSize());
    275         byte[] data = new byte[bdi.getDataSize()];
    276         bdi.readEntityData(data, 0, bdi.getDataSize());
    277         assertEquals("payload size is wrong",
    278                 expectedData.length, data.length);
    279         for (int i = 0; i < data.length; i++) {
    280             assertEquals("payload mismatch",
    281                     expectedData[i], data[i]);
    282         }
    283     }
    284     private void readAndVerifyDeletedEntity(BackupDataInput bdi, String expectedKey)
    285             throws IOException {
    286         assertEquals("Key mismatch",
    287                 expectedKey, bdi.getKey());
    288         assertEquals("deletion mis-reported",
    289                 -1, bdi.getDataSize());
    290     }
    291 }
    292