1/* 2 * Copyright (C) 2015 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 static org.mockito.Mockito.mock; 20import static org.mockito.Mockito.when; 21 22import android.content.Context; 23import android.test.AndroidTestCase; 24import android.util.ArrayMap; 25import android.util.ArraySet; 26 27import org.xmlpull.v1.XmlPullParser; 28import org.xmlpull.v1.XmlPullParserException; 29import org.xmlpull.v1.XmlPullParserFactory; 30 31import java.io.File; 32import java.io.StringReader; 33import java.util.ArrayList; 34import java.util.Collections; 35import java.util.List; 36import java.util.Map; 37import java.util.Set; 38 39public class FullBackupTest extends AndroidTestCase { 40 private XmlPullParserFactory mFactory; 41 private XmlPullParser mXpp; 42 private Context mContext; 43 44 Map<String, Set<String>> includeMap; 45 Set<String> excludesSet; 46 47 @Override 48 public void setUp() throws Exception { 49 mFactory = XmlPullParserFactory.newInstance(); 50 mXpp = mFactory.newPullParser(); 51 mContext = getContext(); 52 53 includeMap = new ArrayMap(); 54 excludesSet = new ArraySet(); 55 } 56 57 public void testparseBackupSchemeFromXml_onlyInclude() throws Exception { 58 mXpp.setInput(new StringReader( 59 "<full-backup-content>" + 60 "<include path=\"onlyInclude.txt\" domain=\"file\"/>" + 61 "</full-backup-content>")); 62 63 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 64 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 65 66 assertEquals("Excluding files when there was no <exclude/> tag.", 0, excludesSet.size()); 67 assertEquals("Unexpected number of <include/>s", 1, includeMap.size()); 68 69 Set<String> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN); 70 assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size()); 71 assertEquals("Invalid path parsed for <include/>", 72 new File(mContext.getFilesDir(), "onlyInclude.txt").getCanonicalPath(), 73 fileDomainIncludes.iterator().next()); 74 } 75 76 public void testparseBackupSchemeFromXml_onlyExclude() throws Exception { 77 mXpp.setInput(new StringReader( 78 "<full-backup-content>" + 79 "<exclude path=\"onlyExclude.txt\" domain=\"file\"/>" + 80 "</full-backup-content>")); 81 82 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 83 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 84 85 assertEquals("Including files when there was no <include/> tag.", 0, includeMap.size()); 86 assertEquals("Unexpected number of <exclude/>s", 1, excludesSet.size()); 87 assertEquals("Invalid path parsed for <exclude/>", 88 new File(mContext.getFilesDir(), "onlyExclude.txt").getCanonicalPath(), 89 excludesSet.iterator().next()); 90 } 91 92 public void testparseBackupSchemeFromXml_includeAndExclude() throws Exception { 93 mXpp.setInput(new StringReader( 94 "<full-backup-content>" + 95 "<exclude path=\"exclude.txt\" domain=\"file\"/>" + 96 "<include path=\"include.txt\" domain=\"file\"/>" + 97 "</full-backup-content>")); 98 99 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 100 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 101 102 Set<String> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN); 103 assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size()); 104 assertEquals("Invalid path parsed for <include/>", 105 new File(mContext.getFilesDir(), "include.txt").getCanonicalPath(), 106 fileDomainIncludes.iterator().next()); 107 108 assertEquals("Unexpected number of <exclude/>s", 1, excludesSet.size()); 109 assertEquals("Invalid path parsed for <exclude/>", 110 new File(mContext.getFilesDir(), "exclude.txt").getCanonicalPath(), 111 excludesSet.iterator().next()); 112 } 113 114 public void testparseBackupSchemeFromXml_lotsOfIncludesAndExcludes() throws Exception { 115 mXpp.setInput(new StringReader( 116 "<full-backup-content>" + 117 "<exclude path=\"exclude1.txt\" domain=\"file\"/>" + 118 "<include path=\"include1.txt\" domain=\"file\"/>" + 119 "<exclude path=\"exclude2.txt\" domain=\"database\"/>" + 120 "<include path=\"include2.txt\" domain=\"database\"/>" + 121 "<exclude path=\"exclude3\" domain=\"sharedpref\"/>" + 122 "<include path=\"include3\" domain=\"sharedpref\"/>" + 123 "<exclude path=\"exclude4.xml\" domain=\"sharedpref\"/>" + 124 "<include path=\"include4.xml\" domain=\"sharedpref\"/>" + 125 "</full-backup-content>")); 126 127 128 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 129 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 130 131 Set<String> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN); 132 assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size()); 133 assertEquals("Invalid path parsed for <include/>", 134 new File(mContext.getFilesDir(), "include1.txt").getCanonicalPath(), 135 fileDomainIncludes.iterator().next()); 136 137 Set<String> databaseDomainIncludes = includeMap.get(FullBackup.DATABASE_TREE_TOKEN); 138 // Three expected here because of "-journal" and "-wal" files 139 assertEquals("Didn't find expected database domain include.", 140 3, databaseDomainIncludes.size()); 141 assertTrue("Invalid path parsed for <include/>", 142 databaseDomainIncludes.contains( 143 new File(mContext.getDatabasePath("foo").getParentFile(), "include2.txt") 144 .getCanonicalPath())); 145 assertTrue("Invalid path parsed for <include/>", 146 databaseDomainIncludes.contains( 147 new File( 148 mContext.getDatabasePath("foo").getParentFile(), 149 "include2.txt-journal") 150 .getCanonicalPath())); 151 assertTrue("Invalid path parsed for <include/>", 152 databaseDomainIncludes.contains( 153 new File( 154 mContext.getDatabasePath("foo").getParentFile(), 155 "include2.txt-wal") 156 .getCanonicalPath())); 157 158 List<String> sharedPrefDomainIncludes = new ArrayList<String>( 159 includeMap.get(FullBackup.SHAREDPREFS_TREE_TOKEN)); 160 Collections.sort(sharedPrefDomainIncludes); 161 162 assertEquals("Didn't find expected sharedpref domain include.", 163 3, sharedPrefDomainIncludes.size()); 164 assertEquals("Invalid path parsed for <include/>", 165 new File(mContext.getSharedPrefsFile("foo").getParentFile(), "include3") 166 .getCanonicalPath(), 167 sharedPrefDomainIncludes.get(0)); 168 assertEquals("Invalid path parsed for <include/>", 169 new File(mContext.getSharedPrefsFile("foo").getParentFile(), "include3.xml") 170 .getCanonicalPath(), 171 sharedPrefDomainIncludes.get(1)); 172 assertEquals("Invalid path parsed for <include/>", 173 new File(mContext.getSharedPrefsFile("foo").getParentFile(), "include4.xml") 174 .getCanonicalPath(), 175 sharedPrefDomainIncludes.get(2)); 176 177 178 assertEquals("Unexpected number of <exclude/>s", 7, excludesSet.size()); 179 // Sets are annoying to iterate over b/c order isn't enforced - convert to an array and 180 // sort lexicographically. 181 List<String> arrayedSet = new ArrayList<String>(excludesSet); 182 Collections.sort(arrayedSet); 183 184 assertEquals("Invalid path parsed for <exclude/>", 185 new File(mContext.getDatabasePath("foo").getParentFile(), "exclude2.txt") 186 .getCanonicalPath(), 187 arrayedSet.get(0)); 188 assertEquals("Invalid path parsed for <exclude/>", 189 new File(mContext.getDatabasePath("foo").getParentFile(), "exclude2.txt-journal") 190 .getCanonicalPath(), 191 arrayedSet.get(1)); 192 assertEquals("Invalid path parsed for <exclude/>", 193 new File(mContext.getDatabasePath("foo").getParentFile(), "exclude2.txt-wal") 194 .getCanonicalPath(), 195 arrayedSet.get(2)); 196 assertEquals("Invalid path parsed for <exclude/>", 197 new File(mContext.getFilesDir(), "exclude1.txt").getCanonicalPath(), 198 arrayedSet.get(3)); 199 assertEquals("Invalid path parsed for <exclude/>", 200 new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude3") 201 .getCanonicalPath(), 202 arrayedSet.get(4)); 203 assertEquals("Invalid path parsed for <exclude/>", 204 new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude3.xml") 205 .getCanonicalPath(), 206 arrayedSet.get(5)); 207 assertEquals("Invalid path parsed for <exclude/>", 208 new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude4.xml") 209 .getCanonicalPath(), 210 arrayedSet.get(6)); 211 } 212 213 public void testParseBackupSchemeFromXml_invalidXmlFails() throws Exception { 214 // Invalid root tag. 215 mXpp.setInput(new StringReader( 216 "<full-weird-tag>" + 217 "<exclude path=\"invalidRootTag.txt\" domain=\"file\"/>" + 218 "</ffull-weird-tag>" )); 219 220 try { 221 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 222 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 223 fail("Invalid root xml tag should throw an XmlPullParserException"); 224 } catch (XmlPullParserException expected) {} 225 226 // Invalid exclude tag. 227 mXpp.setInput(new StringReader( 228 "<full-backup-content>" + 229 "<excluded path=\"invalidExcludeTag.txt\" domain=\"file\"/>" + 230 "</full-backup-conten>t" )); 231 try { 232 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 233 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 234 fail("Misspelled xml exclude tag should throw an XmlPullParserException"); 235 } catch (XmlPullParserException expected) {} 236 237 // Just for good measure - invalid include tag. 238 mXpp.setInput(new StringReader( 239 "<full-backup-content>" + 240 "<yinclude path=\"invalidIncludeTag.txt\" domain=\"file\"/>" + 241 "</full-backup-conten>t" )); 242 try { 243 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 244 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 245 fail("Misspelled xml exclude tag should throw an XmlPullParserException"); 246 } catch (XmlPullParserException expected) {} 247 248 } 249 250 public void testInvalidPath_doesNotBackup() throws Exception { 251 mXpp.setInput(new StringReader( 252 "<full-backup-content>" + 253 "<exclude path=\"..\" domain=\"file\"/>" + // Invalid use of ".." dir. 254 "</full-backup-content>" )); 255 256 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 257 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 258 259 assertEquals("Didn't throw away invalid \"..\" path.", 0, includeMap.size()); 260 261 Set<String> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN); 262 assertNull("Didn't throw away invalid \"..\" path.", fileDomainIncludes); 263 } 264 public void testDoubleDotInPath_isIgnored() throws Exception { 265 mXpp.setInput(new StringReader( 266 "<full-backup-content>" + 267 "<include path=\"..\" domain=\"file\"/>" + // Invalid use of ".." dir. 268 "</full-backup-content>" )); 269 270 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 271 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 272 273 assertEquals("Didn't throw away invalid \"..\" path.", 0, includeMap.size()); 274 275 Set<String> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN); 276 assertNull("Didn't throw away invalid \"..\" path.", fileDomainIncludes); 277 } 278 279 public void testDoubleSlashInPath_isIgnored() throws Exception { 280 mXpp.setInput(new StringReader( 281 "<full-backup-content>" + 282 "<exclude path=\"//hello.txt\" domain=\"file\"/>" + // Invalid use of "//" 283 "</full-backup-content>" )); 284 285 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 286 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 287 288 assertEquals("Didn't throw away invalid path containing \"//\".", 0, excludesSet.size()); 289 } 290} 291