blob: 325b60573d490607ced143b574c52ea51638f331 [file] [log] [blame]
/*
* 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 libcore.tzdata.update2;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import libcore.io.IoUtils;
import libcore.io.Streams;
import libcore.tzdata.shared2.DistroVersion;
import libcore.tzdata.shared2.FileUtils;
import libcore.tzdata.shared2.TimeZoneDistro;
import libcore.tzdata.testing.ZoneInfoTestHelper;
import libcore.tzdata.update2.tools.TimeZoneDistroBuilder;
/**
* Tests for {@link TimeZoneDistroInstaller}.
*/
public class TimeZoneDistroInstallerTest extends TestCase {
// OLDER_RULES_VERSION < SYSTEM_RULES_VERSION < NEW_RULES_VERSION < NEWER_RULES_VERSION
private static final String OLDER_RULES_VERSION = "2030a";
private static final String SYSTEM_RULES_VERSION = "2030b";
private static final String NEW_RULES_VERSION = "2030c";
private static final String NEWER_RULES_VERSION = "2030d";
private TimeZoneDistroInstaller installer;
private File tempDir;
private File testInstallDir;
private File testSystemTzDataDir;
@Override
public void setUp() throws Exception {
super.setUp();
tempDir = createDirectory("tempDir");
testInstallDir = createDirectory("testInstall");
testSystemTzDataDir = createDirectory("testSystemTzData");
// Create a file to represent the tzdata file in the /system partition of the device.
File testSystemTzDataFile = new File(testSystemTzDataDir, "tzdata");
byte[] systemTzDataBytes = createTzData(SYSTEM_RULES_VERSION);
createFile(testSystemTzDataFile, systemTzDataBytes);
installer = new TimeZoneDistroInstaller(
"TimeZoneDistroInstallerTest", testSystemTzDataFile, testInstallDir);
}
private static File createDirectory(String prefix) throws Exception {
File dir = File.createTempFile(prefix, "");
assertTrue(dir.delete());
assertTrue(dir.mkdir());
return dir;
}
@Override
public void tearDown() throws Exception {
if (testSystemTzDataDir.exists()) {
FileUtils.deleteRecursive(testInstallDir);
}
if (testInstallDir.exists()) {
FileUtils.deleteRecursive(testInstallDir);
}
if (tempDir.exists()) {
FileUtils.deleteRecursive(tempDir);
}
super.tearDown();
}
/** Tests the an update on a device will fail if the /system tzdata file cannot be found. */
public void testInstall_badSystemFile() throws Exception {
File doesNotExist = new File(testSystemTzDataDir, "doesNotExist");
TimeZoneDistroInstaller brokenSystemInstaller = new TimeZoneDistroInstaller(
"TimeZoneDistroInstallerTest", doesNotExist, testInstallDir);
TimeZoneDistro tzData = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
try {
brokenSystemInstaller.installWithErrorCode(tzData.getBytes());
fail();
} catch (IOException expected) {}
assertNoContentInstalled();
}
/** Tests the first successful update on a device */
public void testInstall_successfulFirstUpdate() throws Exception {
TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro.getBytes()));
assertDistroInstalled(distro);
}
/**
* Tests we can install an update the same version as is in /system.
*/
public void testInstall_successfulFirstUpdate_sameVersionAsSystem() throws Exception {
TimeZoneDistro distro = createValidTimeZoneDistro(SYSTEM_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro.getBytes()));
assertDistroInstalled(distro);
}
/**
* Tests we cannot install an update older than the version in /system.
*/
public void testInstall_unsuccessfulFirstUpdate_olderVersionThanSystem() throws Exception {
TimeZoneDistro distro = createValidTimeZoneDistro(OLDER_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
/**
* Tests an update on a device when there is a prior update already applied.
*/
public void testInstall_successfulFollowOnUpdate_newerVersion() throws Exception {
TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro1.getBytes()));
assertDistroInstalled(distro1);
TimeZoneDistro distro2 = createValidTimeZoneDistro(NEW_RULES_VERSION, 2);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro2.getBytes()));
assertDistroInstalled(distro2);
TimeZoneDistro distro3 = createValidTimeZoneDistro(NEWER_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro3.getBytes()));
assertDistroInstalled(distro3);
}
/**
* Tests an update on a device when there is a prior update already applied, but the follow
* on update is older than in /system.
*/
public void testInstall_unsuccessfulFollowOnUpdate_olderVersion() throws Exception {
TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 2);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro1.getBytes()));
assertDistroInstalled(distro1);
TimeZoneDistro distro2 = createValidTimeZoneDistro(OLDER_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD,
installer.installWithErrorCode(distro2.getBytes()));
assertDistroInstalled(distro1);
}
/** Tests that a distro with a missing file will not update the content. */
public void testInstall_missingTzDataFile() throws Exception {
TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(installedDistro.getBytes()));
assertDistroInstalled(installedDistro);
TimeZoneDistro incompleteDistro =
createValidTimeZoneDistroBuilder(NEWER_RULES_VERSION, 1)
.clearTzDataForTests()
.buildUnvalidated();
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(incompleteDistro.getBytes()));
assertDistroInstalled(installedDistro);
}
/** Tests that a distro with a missing file will not update the content. */
public void testInstall_missingIcuFile() throws Exception {
TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(installedDistro.getBytes()));
assertDistroInstalled(installedDistro);
TimeZoneDistro incompleteDistro =
createValidTimeZoneDistroBuilder(NEWER_RULES_VERSION, 1)
.clearIcuDataForTests()
.buildUnvalidated();
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(incompleteDistro.getBytes()));
assertDistroInstalled(installedDistro);
}
/**
* Tests that an update will be unpacked even if there is a partial update from a previous run.
*/
public void testInstall_withWorkingDir() throws Exception {
File workingDir = installer.getWorkingDir();
assertTrue(workingDir.mkdir());
createFile(new File(workingDir, "myFile"), new byte[] { 'a' });
TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro.getBytes()));
assertDistroInstalled(distro);
}
/**
* Tests that a distro without a distro version file will be rejected.
*/
public void testInstall_withMissingDistroVersionFile() throws Exception {
// Create a distro without a version file.
TimeZoneDistro distro = createValidTimeZoneDistroBuilder(NEW_RULES_VERSION, 1)
.clearVersionForTests()
.buildUnvalidated();
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
/**
* Tests that a distro with an newer distro version will be rejected.
*/
public void testInstall_withNewerDistroVersion() throws Exception {
// Create a distro that will appear to be newer than the one currently supported.
TimeZoneDistro distro = createValidTimeZoneDistroBuilder(NEW_RULES_VERSION, 1)
.replaceFormatVersionForTests(2, 1)
.buildUnvalidated();
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
/**
* Tests that a distro with a badly formed distro version will be rejected.
*/
public void testInstall_withBadlyFormedDistroVersion() throws Exception {
// Create a distro that has an invalid major distro version. It should be 3 numeric
// characters, "." and 3 more numeric characters.
DistroVersion validDistroVersion = new DistroVersion(1, 1, NEW_RULES_VERSION, 1);
byte[] invalidFormatVersionBytes = validDistroVersion.toBytes();
invalidFormatVersionBytes[0] = 'A';
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidFormatVersionBytes);
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
/**
* Tests that a distro with a badly formed revision will be rejected.
*/
public void testInstall_withBadlyFormedRevision() throws Exception {
// Create a distro that has an invalid revision. It should be 3 numeric characters.
DistroVersion validDistroVersion = new DistroVersion(1, 1, NEW_RULES_VERSION, 1);
byte[] invalidRevisionBytes = validDistroVersion.toBytes();
invalidRevisionBytes[invalidRevisionBytes.length - 3] = 'A';
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidRevisionBytes);
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
/**
* Tests that a distro with a badly formed rules version will be rejected.
*/
public void testInstall_withBadlyFormedRulesVersion() throws Exception {
// Create a distro that has an invalid rules version. It should be in the form "2016c".
DistroVersion validDistroVersion = new DistroVersion(1, 1, NEW_RULES_VERSION, 1);
byte[] invalidRulesVersionBytes = validDistroVersion.toBytes();
invalidRulesVersionBytes[invalidRulesVersionBytes.length - 6] = 'B';
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidRulesVersionBytes);
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
public void testUninstall_noExistingDataDistro() throws Exception {
assertFalse(installer.uninstall());
assertNoContentInstalled();
}
public void testUninstall_existingDataDistro() throws Exception {
File currentDataDir = installer.getCurrentTzDataDir();
assertTrue(currentDataDir.mkdir());
assertTrue(installer.uninstall());
assertNoContentInstalled();
}
public void testUninstall_oldDirsAlreadyExists() throws Exception {
File oldTzDataDir = installer.getOldTzDataDir();
assertTrue(oldTzDataDir.mkdir());
File currentDataDir = installer.getCurrentTzDataDir();
assertTrue(currentDataDir.mkdir());
assertTrue(installer.uninstall());
assertNoContentInstalled();
}
public void testGetSystemRulesVersion() throws Exception {
assertEquals(SYSTEM_RULES_VERSION, installer.getSystemRulesVersion());
}
private static TimeZoneDistro createValidTimeZoneDistro(
String rulesVersion, int revision) throws Exception {
return createValidTimeZoneDistroBuilder(rulesVersion, revision).build();
}
private static TimeZoneDistroBuilder createValidTimeZoneDistroBuilder(
String rulesVersion, int revision) throws Exception {
byte[] bionicTzData = createTzData(rulesVersion);
byte[] icuData = new byte[] { 'a' };
DistroVersion distroVersion = new DistroVersion(
DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
DistroVersion.CURRENT_FORMAT_MINOR_VERSION,
rulesVersion,
revision);
return new TimeZoneDistroBuilder()
.setDistroVersion(distroVersion)
.setTzData(bionicTzData)
.setIcuData(icuData);
}
private void assertDistroInstalled(TimeZoneDistro expectedDistro) throws Exception {
assertTrue(testInstallDir.exists());
File currentTzDataDir = installer.getCurrentTzDataDir();
assertTrue(currentTzDataDir.exists());
File distroVersionFile =
new File(currentTzDataDir, TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
assertTrue(distroVersionFile.exists());
File bionicFile = new File(currentTzDataDir, TimeZoneDistro.TZDATA_FILE_NAME);
assertTrue(bionicFile.exists());
File icuFile = new File(currentTzDataDir, TimeZoneDistro.ICU_DATA_FILE_NAME);
assertTrue(icuFile.exists());
// Assert getInstalledDistroVersion() is reporting correctly.
assertEquals(expectedDistro.getDistroVersion(), installer.getInstalledDistroVersion());
try (ZipInputStream zis = new ZipInputStream(
new ByteArrayInputStream(expectedDistro.getBytes()))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
String entryName = entry.getName();
File actualFile;
if (entryName.endsWith(TimeZoneDistro.DISTRO_VERSION_FILE_NAME)) {
actualFile = distroVersionFile;
} else if (entryName.endsWith(TimeZoneDistro.ICU_DATA_FILE_NAME)) {
actualFile = icuFile;
} else if (entryName.endsWith(TimeZoneDistro.TZDATA_FILE_NAME)) {
actualFile = bionicFile;
} else {
throw new AssertionFailedError("Unknown file found");
}
assertContentsMatches(zis, actualFile);
}
}
// Also check no working directory is left lying around.
File workingDir = installer.getWorkingDir();
assertFalse(workingDir.exists());
}
private void assertContentsMatches(InputStream expected, File actual)
throws Exception {
byte[] actualBytes = IoUtils.readFileAsByteArray(actual.getPath());
byte[] expectedBytes = Streams.readFullyNoClose(expected);
assertTrue(Arrays.equals(expectedBytes, actualBytes));
}
private void assertNoContentInstalled() throws Exception {
assertNull(installer.getInstalledDistroVersion());
File currentTzDataDir = installer.getCurrentTzDataDir();
assertFalse(currentTzDataDir.exists());
// Also check no working directories are left lying around.
File workingDir = installer.getWorkingDir();
assertFalse(workingDir.exists());
File oldDataDir = installer.getOldTzDataDir();
assertFalse(oldDataDir.exists());
}
private static byte[] createTzData(String rulesVersion) {
return new ZoneInfoTestHelper.TzDataBuilder()
.initializeToValid()
.setHeaderMagic("tzdata" + rulesVersion)
.build();
}
private static void createFile(File file, byte[] bytes) {
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(bytes);
} catch (IOException e) {
fail(e.getMessage());
}
}
/**
* Creates a TimeZoneDistro containing arbitrary bytes in the version file. Used for testing
* distros with badly formed version info.
*/
private static TimeZoneDistro createTimeZoneDistroWithVersionBytes(byte[] versionBytes)
throws Exception {
// Create a valid distro, then manipulate the version file.
TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
byte[] distroBytes = distro.getBytes();
ByteArrayOutputStream baos = new ByteArrayOutputStream(distroBytes.length);
try (ZipInputStream zipInputStream =
new ZipInputStream(new ByteArrayInputStream(distroBytes));
ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
zipOutputStream.putNextEntry(entry);
if (entry.getName().equals(TimeZoneDistro.DISTRO_VERSION_FILE_NAME)) {
// Replace the content.
zipOutputStream.write(versionBytes);
} else {
// Just copy the content.
Streams.copy(zipInputStream, zipOutputStream);
}
zipOutputStream.closeEntry();
zipInputStream.closeEntry();
}
}
return new TimeZoneDistro(baos.toByteArray());
}
}