/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.backup;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.test.AndroidTestCase;
import android.util.ArrayMap;
import android.util.ArraySet;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class FullBackupTest extends AndroidTestCase {
private XmlPullParserFactory mFactory;
private XmlPullParser mXpp;
private Context mContext;
Map<String, Set<String>> includeMap;
Set<String> excludesSet;
@Override
public void setUp() throws Exception {
mFactory = XmlPullParserFactory.newInstance();
mXpp = mFactory.newPullParser();
mContext = getContext();
includeMap = new ArrayMap();
excludesSet = new ArraySet();
}
public void testparseBackupSchemeFromXml_onlyInclude() throws Exception {
mXpp.setInput(new StringReader(
"<full-backup-content>" +
"<include path=\"onlyInclude.txt\" domain=\"file\"/>" +
"</full-backup-content>"));
FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
assertEquals("Excluding files when there was no <exclude/> tag.", 0, excludesSet.size());
assertEquals("Unexpected number of <include/>s", 1, includeMap.size());
Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN);
assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size());
assertEquals("Invalid path parsed for <include/>",
new File(mContext.getFilesDir(), "onlyInclude.txt").getCanonicalPath(),
fileDomainIncludes.iterator().next());
}
public void testparseBackupSchemeFromXml_onlyExclude() throws Exception {
mXpp.setInput(new StringReader(
"<full-backup-content>" +
"<exclude path=\"onlyExclude.txt\" domain=\"file\"/>" +
"</full-backup-content>"));
FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
assertEquals("Including files when there was no <include/> tag.", 0, includeMap.size());
assertEquals("Unexpected number of <exclude/>s", 1, excludesSet.size());
assertEquals("Invalid path parsed for <exclude/>",
new File(mContext.getFilesDir(), "onlyExclude.txt").getCanonicalPath(),
excludesSet.iterator().next());
}
public void testparseBackupSchemeFromXml_includeAndExclude() throws Exception {
mXpp.setInput(new StringReader(
"<full-backup-content>" +
"<exclude path=\"exclude.txt\" domain=\"file\"/>" +
"<include path=\"include.txt\" domain=\"file\"/>" +
"</full-backup-content>"));
FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN);
assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size());
assertEquals("Invalid path parsed for <include/>",
new File(mContext.getFilesDir(), "include.txt").getCanonicalPath(),
fileDomainIncludes.iterator().next());
assertEquals("Unexpected number of <exclude/>s", 1, excludesSet.size());
assertEquals("Invalid path parsed for <exclude/>",
new File(mContext.getFilesDir(), "exclude.txt").getCanonicalPath(),
excludesSet.iterator().next());
}
public void testparseBackupSchemeFromXml_lotsOfIncludesAndExcludes() throws Exception {
mXpp.setInput(new StringReader(
"<full-backup-content>" +
"<exclude path=\"exclude1.txt\" domain=\"file\"/>" +
"<include path=\"include1.txt\" domain=\"file\"/>" +
"<exclude path=\"exclude2.txt\" domain=\"database\"/>" +
"<include path=\"include2.txt\" domain=\"database\"/>" +
"<exclude path=\"exclude3.txt\" domain=\"sharedpref\"/>" +
"<include path=\"include3.txt\" domain=\"sharedpref\"/>" +
"</full-backup-content>"));
FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN);
assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size());
assertEquals("Invalid path parsed for <include/>",
new File(mContext.getFilesDir(), "include1.txt").getCanonicalPath(),
fileDomainIncludes.iterator().next());
Set<String> databaseDomainIncludes = includeMap.get(FullBackup.DATABASE_TREE_TOKEN);
assertEquals("Didn't find expected database domain include.",
2, databaseDomainIncludes.size()); // two expected here because of "-journal" file
assertTrue("Invalid path parsed for <include/>",
databaseDomainIncludes.contains(
new File(mContext.getDatabasePath("foo").getParentFile(), "include2.txt")
.getCanonicalPath()));
assertTrue("Invalid path parsed for <include/>",
databaseDomainIncludes.contains(
new File(
mContext.getDatabasePath("foo").getParentFile(),
"include2.txt-journal")
.getCanonicalPath()));
Set<String> sharedPrefDomainIncludes = includeMap.get(FullBackup.SHAREDPREFS_TREE_TOKEN);
assertEquals("Didn't find expected sharedpref domain include.",
1, sharedPrefDomainIncludes.size());
assertEquals("Invalid path parsed for <include/>",
new File(mContext.getSharedPrefsFile("foo").getParentFile(), "include3.txt")
.getCanonicalPath(),
sharedPrefDomainIncludes.iterator().next());
assertEquals("Unexpected number of <exclude/>s", 4, excludesSet.size());
// Sets are annoying to iterate over b/c order isn't enforced - convert to an array and
// sort lexicographically.
List<String> arrayedSet = new ArrayList<String>(excludesSet);
Collections.sort(arrayedSet);
assertEquals("Invalid path parsed for <exclude/>",
new File(mContext.getDatabasePath("foo").getParentFile(), "exclude2.txt")
.getCanonicalPath(),
arrayedSet.get(0));
assertEquals("Invalid path parsed for <exclude/>",
new File(mContext.getDatabasePath("foo").getParentFile(), "exclude2.txt-journal")
.getCanonicalPath(),
arrayedSet.get(1));
assertEquals("Invalid path parsed for <exclude/>",
new File(mContext.getFilesDir(), "exclude1.txt").getCanonicalPath(),
arrayedSet.get(2));
assertEquals("Invalid path parsed for <exclude/>",
new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude3.txt")
.getCanonicalPath(),
arrayedSet.get(3));
}
public void testParseBackupSchemeFromXml_invalidXmlFails() throws Exception {
// Invalid root tag.
mXpp.setInput(new StringReader(
"<full-weird-tag>" +
"<exclude path=\"invalidRootTag.txt\" domain=\"file\"/>" +
"</ffull-weird-tag>" ));
try {
FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
fail("Invalid root xml tag should throw an XmlPullParserException");
} catch (XmlPullParserException expected) {}
// Invalid exclude tag.
mXpp.setInput(new StringReader(
"<full-backup-content>" +
"<excluded path=\"invalidExcludeTag.txt\" domain=\"file\"/>" +
"</full-backup-conten>t" ));
try {
FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
fail("Misspelled xml exclude tag should throw an XmlPullParserException");
} catch (XmlPullParserException expected) {}
// Just for good measure - invalid include tag.
mXpp.setInput(new StringReader(
"<full-backup-content>" +
"<yinclude path=\"invalidIncludeTag.txt\" domain=\"file\"/>" +
"</full-backup-conten>t" ));
try {
FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
fail("Misspelled xml exclude tag should throw an XmlPullParserException");
} catch (XmlPullParserException expected) {}
}
public void testInvalidPath_doesNotBackup() throws Exception {
mXpp.setInput(new StringReader(
"<full-backup-content>" +
"<exclude path=\"..\" domain=\"file\"/>" + // Invalid use of ".." dir.
"</full-backup-content>" ));
FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
assertEquals("Didn't throw away invalid \"..\" path.", 0, includeMap.size());
Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN);
assertNull("Didn't throw away invalid \"..\" path.", fileDomainIncludes);
}
public void testDoubleDotInPath_isIgnored() throws Exception {
mXpp.setInput(new StringReader(
"<full-backup-content>" +
"<include path=\"..\" domain=\"file\"/>" + // Invalid use of ".." dir.
"</full-backup-content>" ));
FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
assertEquals("Didn't throw away invalid \"..\" path.", 0, includeMap.size());
Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN);
assertNull("Didn't throw away invalid \"..\" path.", fileDomainIncludes);
}
public void testDoubleSlashInPath_isIgnored() throws Exception {
mXpp.setInput(new StringReader(
"<full-backup-content>" +
"<exclude path=\"//hello.txt\" domain=\"file\"/>" + // Invalid use of "//"
"</full-backup-content>" ));
FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
assertEquals("Didn't throw away invalid path containing \"//\".", 0, excludesSet.size());
}
}