blob: 8657dcf7a5ed2f869831bbb68e4348bbded5ef76 [file] [log] [blame]
/*
* Copyright (C) 2013 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 com.android.i18n.test.timezone;
import static com.android.i18n.timezone.ZoneInfoDb.SIZEOF_INDEX_ENTRY;
import android.icu.testsharding.MainTestShard;
import android.icu.util.TimeZone;
import com.android.i18n.timezone.TimeZoneDataFiles;
import com.android.i18n.timezone.ZoneInfoData;
import com.android.i18n.timezone.ZoneInfoDb;
import java.io.File;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import libcore.timezone.testing.ZoneInfoTestHelper;
@MainTestShard
public class ZoneInfoDbTest extends junit.framework.TestCase {
// The base tzdata file, always present on a device.
private static final String TZDATA_FILE =
TimeZoneDataFiles.getTimeZoneModuleTzFile(ZoneInfoDb.TZDATA_FILE_NAME);
// An empty override file should fall back to the default file.
public void testLoadTzDataWithFallback_emptyOverrideFile() throws Exception {
String emptyFilePath = makeEmptyFile().getPath();
try (ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE);
ZoneInfoDb dataWithEmptyOverride =
ZoneInfoDb.loadTzDataWithFallback(emptyFilePath, TZDATA_FILE)) {
assertEquals(data.getVersion(), dataWithEmptyOverride.getVersion());
assertEquals(data.getAvailableIDs().length, dataWithEmptyOverride.getAvailableIDs().length);
}
}
// A corrupt override file should fall back to the default file.
public void testLoadTzDataWithFallback_corruptOverrideFile() throws Exception {
String corruptFilePath = makeCorruptFile().getPath();
try (ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE);
ZoneInfoDb dataWithCorruptOverride =
ZoneInfoDb.loadTzDataWithFallback(corruptFilePath, TZDATA_FILE)) {
assertEquals(data.getVersion(), dataWithCorruptOverride.getVersion());
assertEquals(data.getAvailableIDs().length, dataWithCorruptOverride.getAvailableIDs().length);
}
}
// Given no tzdata files we can use, we should fall back to built-in "GMT".
public void testLoadTzDataWithFallback_noGoodFile() throws Exception {
String emptyFilePath = makeEmptyFile().getPath();
try (ZoneInfoDb data = ZoneInfoDb.loadTzDataWithFallback(emptyFilePath)) {
assertEquals("missing", data.getVersion());
assertEquals(1, data.getAvailableIDs().length);
assertEquals("GMT", data.getAvailableIDs()[0]);
}
}
// Given a valid override file, we should find ourselves using that.
public void testLoadTzDataWithFallback_goodOverrideFile() throws Exception {
RandomAccessFile in = new RandomAccessFile(TZDATA_FILE, "r");
byte[] content = new byte[(int) in.length()];
in.readFully(content);
in.close();
// Bump the version number to one long past where humans will be extinct.
content[6] = '9';
content[7] = '9';
content[8] = '9';
content[9] = '9';
content[10] = 'z';
File goodFile = makeTemporaryFile(content);
try (ZoneInfoDb dataWithOverride =
ZoneInfoDb.loadTzDataWithFallback(goodFile.getPath(), TZDATA_FILE);
ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE)) {
assertEquals("9999z", dataWithOverride.getVersion());
assertEquals(data.getAvailableIDs().length, dataWithOverride.getAvailableIDs().length);
} finally {
goodFile.delete();
}
}
public void testLoadTzData_badHeader() throws Exception {
RandomAccessFile in = new RandomAccessFile(TZDATA_FILE, "r");
byte[] content = new byte[(int) in.length()];
in.readFully(content);
in.close();
// Break the header.
content[0] = 'a';
checkInvalidDataDetected(content);
}
public void testLoadTzData_validTestData() throws Exception {
byte[] data = new ZoneInfoTestHelper.TzDataBuilder().initializeToValid().build();
File testFile = makeTemporaryFile(data);
try {
assertNotNull(ZoneInfoDb.loadTzData(testFile.getPath()));
} finally {
testFile.delete();
}
}
public void testLoadTzData_invalidOffsets() throws Exception {
ZoneInfoTestHelper.TzDataBuilder builder =
new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
// Sections must be in the correct order: section sizing is calculated using them.
builder.setIndexOffsetOverride(10);
builder.setDataOffsetOverride(30);
byte[] data = builder.build();
// The offsets must all be under the total size of the file for this test to be valid.
assertTrue(30 < data.length);
checkInvalidDataDetected(data);
}
public void testLoadTzData_zoneTabOffsetOutsideFile() throws Exception {
ZoneInfoTestHelper.TzDataBuilder builder =
new ZoneInfoTestHelper.TzDataBuilder()
.initializeToValid();
builder.setZoneTabOffsetOverride(3000); // This is invalid if it is outside of the file.
byte[] data = builder.build();
// The zoneTab offset must be outside of the file for this test to be valid.
assertTrue(3000 > data.length);
checkInvalidDataDetected(data);
}
public void testLoadTzData_finalOffsetOutsideFile() throws Exception {
ZoneInfoTestHelper.TzDataBuilder builder =
new ZoneInfoTestHelper.TzDataBuilder()
.initializeToValid();
builder.setFinalOffsetOverride(3000); // This is invalid if it is outside of the file.
byte[] data = builder.build();
// The zoneTab offset must be outside of the file for this test to be valid.
assertTrue(3000 > data.length);
checkInvalidDataDetected(data);
}
public void testLoadTzData_nonDivisibleIndex() throws Exception {
ZoneInfoTestHelper.TzDataBuilder builder =
new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
// Sections must be in the correct order: section sizing is calculated using them.
int indexOffset = 10;
builder.setIndexOffsetOverride(indexOffset);
int dataOffset = indexOffset + ZoneInfoDb.SIZEOF_INDEX_ENTRY - 1;
builder.setDataOffsetOverride(dataOffset);
builder.setZoneTabOffsetOverride(dataOffset + 40);
byte[] data = builder.build();
// The zoneTab offset must be outside of the file for this test to be valid.
assertTrue(3000 > data.length);
checkInvalidDataDetected(data);
}
public void testLoadTzData_badId() throws Exception {
ZoneInfoTestHelper.TzDataBuilder builder =
new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
builder.clearZicData();
byte[] validZicData =
new ZoneInfoTestHelper.ZicDataBuilder().initializeToValid().build();
builder.addZicData("", validZicData); // "" is an invalid ID
checkInvalidDataDetected(builder.build());
}
public void testLoadTzData_badIdOrder() throws Exception {
ZoneInfoTestHelper.TzDataBuilder builder =
new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
builder.clearZicData();
byte[] validZicData =
new ZoneInfoTestHelper.ZicDataBuilder().initializeToValid().build();
builder.addZicData("Europe/Zurich", validZicData);
builder.addZicData("Europe/London", validZicData);
checkInvalidDataDetected(builder.build());
}
public void testLoadTzData_duplicateId() throws Exception {
ZoneInfoTestHelper.TzDataBuilder builder =
new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
builder.clearZicData();
byte[] validZicData =
new ZoneInfoTestHelper.ZicDataBuilder().initializeToValid().build();
builder.addZicData("Europe/London", validZicData);
builder.addZicData("Europe/London", validZicData);
checkInvalidDataDetected(builder.build());
}
public void testLoadTzData_badZicLength() throws Exception {
ZoneInfoTestHelper.TzDataBuilder builder =
new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
builder.clearZicData();
byte[] invalidZicData = "This is too short".getBytes();
builder.addZicData("Europe/London", invalidZicData);
checkInvalidDataDetected(builder.build());
}
private static void checkInvalidDataDetected(byte[] data) throws Exception {
File testFile = makeTemporaryFile(data);
try {
assertNull(ZoneInfoDb.loadTzData(testFile.getPath()));
} finally {
testFile.delete();
}
}
// Confirms any caching that exists correctly handles ZoneInfoData mutability.
public void testMakeTimeZone_timeZoneMutability() throws Exception {
try (ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE)) {
String tzId = "Europe/London";
ZoneInfoData first = data.makeZoneInfoData(tzId);
ZoneInfoData second = data.makeZoneInfoData(tzId);
assertNotSame(first, second);
assertTrue(first.hasSameRules(second));
first.setRawOffset(3600);
assertFalse(first.getRawOffset() == second.getRawOffset());
}
}
public void testMakeTimeZone_notFound() throws Exception {
try (ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE)) {
assertNull(data.makeZoneInfoData("THIS_TZ_DOES_NOT_EXIST"));
assertFalse(data.hasTimeZone("THIS_TZ_DOES_NOT_EXIST"));
}
}
public void testMakeTimeZone_found() throws Exception {
try (ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE)) {
assertNotNull(data.makeZoneInfoData("Europe/London"));
assertTrue(data.hasTimeZone("Europe/London"));
}
}
public void testGetZoneTab() throws Exception {
String zoneTab = "This is my zone.tab";
ZoneInfoTestHelper.TzDataBuilder builder =
new ZoneInfoTestHelper.TzDataBuilder()
.initializeToValid()
.setZoneTab(zoneTab);
byte[] data = builder.build();
File testFile = makeTemporaryFile(data);
ZoneInfoDb zoneInfoDb = ZoneInfoDb.loadTzData(testFile.getPath());
assertEquals(zoneTab, zoneInfoDb.getZoneTab());
}
private static File makeCorruptFile() throws Exception {
return makeTemporaryFile("invalid content".getBytes());
}
private static File makeEmptyFile() throws Exception {
return makeTemporaryFile(new byte[0]);
}
private static File makeTemporaryFile(byte[] content) throws Exception {
File f = File.createTempFile("temp-", ".txt");
FileOutputStream fos = new FileOutputStream(f);
fos.write(content);
fos.close();
return f;
}
}