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