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