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.DATA_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.DATA_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.txt\" domain=\"sharedpref\"/>" + 122 "<include path=\"include3.txt\" domain=\"sharedpref\"/>" + 123 "</full-backup-content>")); 124 125 126 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 127 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 128 129 Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN); 130 assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size()); 131 assertEquals("Invalid path parsed for <include/>", 132 new File(mContext.getFilesDir(), "include1.txt").getCanonicalPath(), 133 fileDomainIncludes.iterator().next()); 134 135 Set<String> databaseDomainIncludes = includeMap.get(FullBackup.DATABASE_TREE_TOKEN); 136 assertEquals("Didn't find expected database domain include.", 137 2, databaseDomainIncludes.size()); // two expected here because of "-journal" file 138 assertTrue("Invalid path parsed for <include/>", 139 databaseDomainIncludes.contains( 140 new File(mContext.getDatabasePath("foo").getParentFile(), "include2.txt") 141 .getCanonicalPath())); 142 assertTrue("Invalid path parsed for <include/>", 143 databaseDomainIncludes.contains( 144 new File( 145 mContext.getDatabasePath("foo").getParentFile(), 146 "include2.txt-journal") 147 .getCanonicalPath())); 148 149 Set<String> sharedPrefDomainIncludes = includeMap.get(FullBackup.SHAREDPREFS_TREE_TOKEN); 150 assertEquals("Didn't find expected sharedpref domain include.", 151 1, sharedPrefDomainIncludes.size()); 152 assertEquals("Invalid path parsed for <include/>", 153 new File(mContext.getSharedPrefsFile("foo").getParentFile(), "include3.txt") 154 .getCanonicalPath(), 155 sharedPrefDomainIncludes.iterator().next()); 156 157 158 assertEquals("Unexpected number of <exclude/>s", 4, excludesSet.size()); 159 // Sets are annoying to iterate over b/c order isn't enforced - convert to an array and 160 // sort lexicographically. 161 List<String> arrayedSet = new ArrayList<String>(excludesSet); 162 Collections.sort(arrayedSet); 163 164 assertEquals("Invalid path parsed for <exclude/>", 165 new File(mContext.getDatabasePath("foo").getParentFile(), "exclude2.txt") 166 .getCanonicalPath(), 167 arrayedSet.get(0)); 168 assertEquals("Invalid path parsed for <exclude/>", 169 new File(mContext.getDatabasePath("foo").getParentFile(), "exclude2.txt-journal") 170 .getCanonicalPath(), 171 arrayedSet.get(1)); 172 assertEquals("Invalid path parsed for <exclude/>", 173 new File(mContext.getFilesDir(), "exclude1.txt").getCanonicalPath(), 174 arrayedSet.get(2)); 175 assertEquals("Invalid path parsed for <exclude/>", 176 new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude3.txt") 177 .getCanonicalPath(), 178 arrayedSet.get(3)); 179 } 180 181 public void testParseBackupSchemeFromXml_invalidXmlFails() throws Exception { 182 // Invalid root tag. 183 mXpp.setInput(new StringReader( 184 "<full-weird-tag>" + 185 "<exclude path=\"invalidRootTag.txt\" domain=\"file\"/>" + 186 "</ffull-weird-tag>" )); 187 188 try { 189 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 190 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 191 fail("Invalid root xml tag should throw an XmlPullParserException"); 192 } catch (XmlPullParserException expected) {} 193 194 // Invalid exclude tag. 195 mXpp.setInput(new StringReader( 196 "<full-backup-content>" + 197 "<excluded path=\"invalidExcludeTag.txt\" domain=\"file\"/>" + 198 "</full-backup-conten>t" )); 199 try { 200 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 201 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 202 fail("Misspelled xml exclude tag should throw an XmlPullParserException"); 203 } catch (XmlPullParserException expected) {} 204 205 // Just for good measure - invalid include tag. 206 mXpp.setInput(new StringReader( 207 "<full-backup-content>" + 208 "<yinclude path=\"invalidIncludeTag.txt\" domain=\"file\"/>" + 209 "</full-backup-conten>t" )); 210 try { 211 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 212 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 213 fail("Misspelled xml exclude tag should throw an XmlPullParserException"); 214 } catch (XmlPullParserException expected) {} 215 216 } 217 218 public void testInvalidPath_doesNotBackup() throws Exception { 219 mXpp.setInput(new StringReader( 220 "<full-backup-content>" + 221 "<exclude path=\"..\" domain=\"file\"/>" + // Invalid use of ".." dir. 222 "</full-backup-content>" )); 223 224 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 225 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 226 227 assertEquals("Didn't throw away invalid \"..\" path.", 0, includeMap.size()); 228 229 Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN); 230 assertNull("Didn't throw away invalid \"..\" path.", fileDomainIncludes); 231 } 232 public void testDoubleDotInPath_isIgnored() throws Exception { 233 mXpp.setInput(new StringReader( 234 "<full-backup-content>" + 235 "<include path=\"..\" domain=\"file\"/>" + // Invalid use of ".." dir. 236 "</full-backup-content>" )); 237 238 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 239 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 240 241 assertEquals("Didn't throw away invalid \"..\" path.", 0, includeMap.size()); 242 243 Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN); 244 assertNull("Didn't throw away invalid \"..\" path.", fileDomainIncludes); 245 } 246 247 public void testDoubleSlashInPath_isIgnored() throws Exception { 248 mXpp.setInput(new StringReader( 249 "<full-backup-content>" + 250 "<exclude path=\"//hello.txt\" domain=\"file\"/>" + // Invalid use of "//" 251 "</full-backup-content>" )); 252 253 FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); 254 bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); 255 256 assertEquals("Didn't throw away invalid path containing \"//\".", 0, excludesSet.size()); 257 } 258} 259