blob: a41b6d207a9bd33d19063a8c97461b447a0c893f [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
#include "exprs/timezone_db.h"
#include "testutil/gtest-util.h"
#include "common/names.h"
using std::ios_base;
namespace impala {
class TimezoneDbNamesTest : public testing::Test {
protected:
void TestValidTimezoneOffset(const string& tz_offset, int64_t expected_offset) {
int64_t result;
EXPECT_TRUE(TimezoneDatabase::IsTimezoneOffsetValid(tz_offset, &result));
EXPECT_EQ(expected_offset, result);
}
void TestInvalidTimezoneOffset(const string& tz_offset) {
int64_t result;
EXPECT_FALSE(TimezoneDatabase::IsTimezoneOffsetValid(tz_offset, &result));
}
void TestValidTimezoneName(const string& tz_name) {
EXPECT_TRUE(TimezoneDatabase::IsTimezoneNameValid(tz_name));
}
void TestInvalidTimezoneName(const string& tz_name) {
EXPECT_FALSE(TimezoneDatabase::IsTimezoneNameValid(tz_name));
}
};
TEST_F(TimezoneDbNamesTest, TzOffset) {
TestValidTimezoneOffset("0", 0);
TestValidTimezoneOffset("+00", 0);
TestValidTimezoneOffset("+1", 1);
TestValidTimezoneOffset("-1234", -1234);
TestValidTimezoneOffset("86399", 86399);
TestValidTimezoneOffset("+86399", 86399);
TestValidTimezoneOffset("-86399", -86399);
// Inalid UTC offsets.
TestInvalidTimezoneOffset("");
TestInvalidTimezoneOffset(".");
TestInvalidTimezoneOffset("0.1");
TestInvalidTimezoneOffset("PST");
TestInvalidTimezoneOffset(":0");
TestInvalidTimezoneOffset("0&");
// UTC offset has to be less then 24 hours in seconds.
TestInvalidTimezoneOffset("86400");
TestInvalidTimezoneOffset("-86400");
}
TEST_F(TimezoneDbNamesTest, TzName) {
TestValidTimezoneName("PST");
TestValidTimezoneName("PST8PDT");
TestValidTimezoneName("UTC+1");
TestValidTimezoneName("UTC-10");
TestValidTimezoneName("GMT-10:00");
TestValidTimezoneName("W-SU");
TestValidTimezoneName("Singapore");
TestValidTimezoneName("Etc/GMT+5");
TestValidTimezoneName("America/Tijuana");
TestValidTimezoneName("America/Argentina/San_Jose");
// Misformatted time-zone names.
TestInvalidTimezoneName("");
TestInvalidTimezoneName(".");
TestInvalidTimezoneName("zone.tab");
TestInvalidTimezoneName("/");
TestInvalidTimezoneName("/PST");
TestInvalidTimezoneName(":GMT-10");
TestInvalidTimezoneName("PST8PDT/");
TestInvalidTimezoneName("America//Tijuana");
TestInvalidTimezoneName("America/Tijuana/");
// Time-zone name segments must start with uppercase letters.
TestInvalidTimezoneName("pST");
TestInvalidTimezoneName("pst");
TestInvalidTimezoneName("singapore");
TestInvalidTimezoneName("america/Tijuana");
TestInvalidTimezoneName("America/tijuana");
TestInvalidTimezoneName("America/Argentina/san_Jose");
TestInvalidTimezoneName("posixrules");
}
class TimezoneDbLoadAliasTest : public testing::Test {
protected:
virtual void SetUp() {
TimezoneDatabase::tz_name_map_["UTC"] =
make_shared<Timezone>(TimezoneDatabase::GetUtcTimezone());
TimezoneDatabase::tz_name_map_["Etc/GMT"] =
make_shared<Timezone>(TimezoneDatabase::GetUtcTimezone());
TimezoneDatabase::tz_name_map_["Etc/GMT-0"] =
make_shared<Timezone>(TimezoneDatabase::GetUtcTimezone());
TimezoneDatabase::tz_name_map_["Zulu"] =
make_shared<Timezone>(TimezoneDatabase::GetUtcTimezone());
}
virtual void TearDown() {
TimezoneDatabase::tz_name_map_.clear();
}
void TestLoadZoneAliasesSuccess(const string& aliases,
const vector<pair<string, string>>& expected_aliases,
const vector<pair<string, int>>& expected_offsets) {
stringstream ss(aliases, ios_base::in);
Status status = TimezoneDatabase::LoadZoneAliases(ss);
EXPECT_TRUE(status.ok());
// Check aliases defined as existing time-zones.
for (const auto& alias : expected_aliases) {
const Timezone* tz1 = TimezoneDatabase::FindTimezone(alias.first);
const Timezone* tz2 = TimezoneDatabase::FindTimezone(alias.second);
EXPECT_TRUE(tz1 != nullptr && tz1 == tz2);
}
// Check aliases defined as UTC offsets.
cctz::time_point<cctz::sys_seconds> epoch =
std::chrono::time_point_cast<cctz::sys_seconds>(
std::chrono::system_clock::from_time_t(0));
for (const auto& offset : expected_offsets) {
const Timezone* tz = TimezoneDatabase::FindTimezone(offset.first);
EXPECT_TRUE(tz != nullptr && tz->lookup(epoch).offset == offset.second);
}
}
void TestLoadZoneAliasesFailure(const string& aliases,
const string& expected_err_msg) {
stringstream ss(aliases, ios_base::in);
Status status = TimezoneDatabase::LoadZoneAliases(ss);
EXPECT_FALSE(status.ok());
ASSERT_NE(string::npos, status.msg().msg().find(expected_err_msg));
}
};
TEST_F(TimezoneDbLoadAliasTest, LoadZoneAliases) {
TestLoadZoneAliasesSuccess(
"# Test time-zone aliases\n"
"# First = Zulu # Entire line is commented out\n"
"First = UTC \n"
"Second = Zulu ### \n"
"Second = UTC # This line will be skipped: Second is already defined\n"
"Third = Etc/GMT \n"
"Fourth =Etc/GMT-0 \n"
"Fourth = 1234 # This line will be skipped: Fourth is already defined\n"
"Fifth = ZULU # This line will be skipped: value is an invalid time-zone name\n"
"Fifth =1234 # Alias defined as UTC offset in seconds\n"
"# Aliases may contain non-alphabetic characters\n"
"+00:00 = Etc/GMT-0 \n"
"Coordinated Universal Time = UTC\n",
{ make_pair("First", "UTC"), make_pair("Second", "Zulu"),
make_pair("Third", "Etc/GMT"), make_pair("Fourth", "Etc/GMT-0"),
make_pair("+00:00", "Etc/GMT-0"),
make_pair("Coordinated Universal Time", "UTC") },
{ make_pair("Fifth", 1234) });
// Value is commented out.
TestLoadZoneAliasesFailure("UTC1= # Zulu\n", "Error in line 1. Missing value.");
// '=' delimiter is missing.
TestLoadZoneAliasesFailure("UTC2 Zulu\n", "Error in line 1. '=' is missing.");
// Missing alias
TestLoadZoneAliasesFailure(" = Zulu\n",
"Error in line 1. Time-zone alias name is missing.");
}
class TimezoneDbLoadZoneInfoTest : public testing::Test {
protected:
void TestLoadZoneInfoSuccess(const string& path) {
EXPECT_OK(TimezoneDatabase::LoadZoneInfo(path));
}
void TestFindTimezoneSuccess(const vector<string>& expected_names) {
// Check that all the expected time-zones have been loaded.
for (const auto& tz_name : expected_names) {
ASSERT_NE(nullptr, TimezoneDatabase::FindTimezone(tz_name));
}
}
void TestFindTimezoneFailure(const vector<string>& expected_names) {
// Check that all the expected time-zones have been loaded.
for (const auto& tz_name : expected_names) {
ASSERT_EQ(nullptr, TimezoneDatabase::FindTimezone(tz_name));
}
}
};
TEST_F(TimezoneDbLoadZoneInfoTest, LoadZoneInfo) {
const string& path = Substitute("$0/testdata/tzdb_tiny", getenv("IMPALA_HOME"));
TestLoadZoneInfoSuccess(path);
// Test that timezones containing non-alphabetic characters were loaded successfully.
TestFindTimezoneSuccess({ "Etc/GMT+4" });
// Test that symbolic links are handled properly.
// "UTC" is a symbolic link to "Zulu". Both should be loaded.
TestFindTimezoneSuccess({ "UTC", "Zulu" });
// "America/New_York" and "US/Eastern" are symbolic links to "posixrules". It is
// expected that "posixrules" is not loaded, but "America/New_York" and "US/Eastern"
// are.
TestFindTimezoneFailure({ "posixrules" });
TestFindTimezoneSuccess({ "America/New_York", "US/Eastern" });
// Test that "posix" directory was skipped.
TestFindTimezoneFailure({ "posix/UTC" });
}
}