aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/testsuite/std/time/tzdb/links.cc
blob: 0ba214846c6e78d24b980e318b9ebbf27e465576 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// { 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();
}