Home | History | Annotate | Download | only in backup
      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 
     17 package android.app.backup;
     18 
     19 import static org.mockito.Mockito.mock;
     20 import static org.mockito.Mockito.when;
     21 
     22 import android.content.Context;
     23 import android.test.AndroidTestCase;
     24 import android.util.ArrayMap;
     25 import android.util.ArraySet;
     26 
     27 import org.xmlpull.v1.XmlPullParser;
     28 import org.xmlpull.v1.XmlPullParserException;
     29 import org.xmlpull.v1.XmlPullParserFactory;
     30 
     31 import java.io.File;
     32 import java.io.StringReader;
     33 import java.util.ArrayList;
     34 import java.util.Collections;
     35 import java.util.List;
     36 import java.util.Map;
     37 import java.util.Set;
     38 
     39 public 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