blob: 0ba214846c6e78d24b980e318b9ebbf27e465576 [file] [log] [blame]
// { dg-do run { target c++20 } }
// { dg-require-effective-target tzdb }
// { dg-require-effective-target cxx11_abi }
// { dg-xfail-run-if "no weak override on AIX" { powerpc-ibm-aix* } }
#include <chrono>
#include <fstream>
#include <testsuite_hooks.h>
static bool override_used = false;
namespace __gnu_cxx
{
const char* zoneinfo_dir_override() {
override_used = true;
return "./";
}
}
using namespace std::chrono;
void
test_link_chains()
{
std::ofstream("tzdata.zi") << R"(# version test_1
Link Greenwich G_M_T
Link Etc/GMT Greenwich
Zone Etc/GMT 0 - GMT
Zone A_Zone 1 - ZON
Link A_Zone L1
Link L1 L2
Link L2 L3
Link L3 L4
Link L4 L5
Link L5 L6
Link L3 L7
)";
const auto& db = reload_tzdb();
VERIFY( override_used ); // If this fails then XFAIL for the target.
VERIFY( db.version == "test_1" );
// Simple case of a link with a zone as its target.
VERIFY( locate_zone("Greenwich")->name() == "Etc/GMT" );
// Chains of links, where the target may be another link.
VERIFY( locate_zone("G_M_T")->name() == "Etc/GMT" );
VERIFY( locate_zone("L1")->name() == "A_Zone" );
VERIFY( locate_zone("L2")->name() == "A_Zone" );
VERIFY( locate_zone("L3")->name() == "A_Zone" );
VERIFY( locate_zone("L4")->name() == "A_Zone" );
VERIFY( locate_zone("L5")->name() == "A_Zone" );
VERIFY( locate_zone("L6")->name() == "A_Zone" );
VERIFY( locate_zone("L7")->name() == "A_Zone" );
}
void
test_bad_links()
{
// The zic(8) man page says
// > the behavior is unspecified if multiple zone or link lines
// > define the same name"
// For libstdc++ the expected behaviour is described and tested below.
std::ofstream("tzdata.zi") << R"(# version test_2
Zone A_Zone 1 - ZA
Zone B_Zone 2 - ZB
Link A_Zone B_Zone
Link B_Zone C_Link
Link C_Link D_Link
Link D_Link E_Link
)";
const auto& db2 = reload_tzdb();
VERIFY( override_used ); // If this fails then XFAIL for the target.
VERIFY( db2.version == "test_2" );
// The standard requires locate_zone(name) to search for a zone first,
// so this finds the zone B_Zone, not the link that points to zone A_Zone.
VERIFY( locate_zone("B_Zone")->name() == "B_Zone" );
// And libstdc++ does the same at every step when following chained links:
VERIFY( locate_zone("C_Link")->name() == "B_Zone" );
VERIFY( locate_zone("D_Link")->name() == "B_Zone" );
VERIFY( locate_zone("E_Link")->name() == "B_Zone" );
// The zic(8) man page says
// > the behavior is unspecified if a chain of one or more links
// > does not terminate in a Zone name.
// For libstdc++ we throw std::runtime_error if locate_zone finds an
// unterminated chain, including the case of a chain that includes a cycle.
std::ofstream("tzdata.zi") << R"(# version test_3
Zone A_Zone 1 - ZON
Link A_Zone GoodLink
Link No_Zone BadLink
Link LinkSelf LinkSelf
Link LinkSelf Link1
Link Link1 Link2
Link Cycle2_A Cycle2_B
Link Cycle2_B Cycle2_A
Link Cycle3_A Cycle3_B
Link Cycle3_B Cycle3_C
Link Cycle3_C Cycle3_A
Link Cycle3_C Cycle3_D
Link Cycle4_A Cycle4_B
Link Cycle4_B Cycle4_C
Link Cycle4_C Cycle4_D
Link Cycle4_D Cycle4_A
)";
const auto& db3 = reload_tzdb();
VERIFY( db3.version == "test_3" );
// Lookup for valid links should still work even if other links are bad.
VERIFY( locate_zone("GoodLink")->name() == "A_Zone" );
#if __cpp_exceptions
try {
locate_zone("BadLink");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("cannot locate zone: BadLink") );
}
// LinkSelf forms a link cycle with itself.
try {
locate_zone("LinkSelf");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("link cycle: LinkSelf") );
}
// Any chain that leads to LinkSelf reaches a cycle.
try {
locate_zone("Link1");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("link cycle: Link1") );
}
try {
locate_zone("Link2");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("link cycle: Link2") );
}
// Cycle2_A and Cycle2_B form a cycle of length two.
try {
locate_zone("Cycle2_A");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("link cycle: Cycle2_A") );
}
try {
locate_zone("Cycle2_B");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("link cycle: Cycle2_B") );
}
// Cycle3_A, Cycle3_B and Cycle3_C form a cycle of length three.
try {
locate_zone("Cycle3_A");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("link cycle: Cycle3_A") );
}
try {
locate_zone("Cycle3_B");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("link cycle: Cycle3_B") );
}
try {
locate_zone("Cycle3_C");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("link cycle: Cycle3_C") );
}
// Cycle3_D isn't part of the cycle, but it leads to it.
try {
locate_zone("Cycle3_D");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("link cycle: Cycle3_D") );
}
// Cycle4_* links form a cycle of length four.
try {
locate_zone("Cycle4_A");
VERIFY( false );
} catch (const std::runtime_error& e) {
std::string_view what(e.what());
VERIFY( what.ends_with("link cycle: Cycle4_A") );
}
#endif
}
int main()
{
test_link_chains();
test_bad_links();
}