| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| #include "clang/InstallAPI/DylibVerifier.h" | ||
| #include "clang/InstallAPI/FrontendRecords.h" | ||
| #include "llvm/Demangle/Demangle.h" | ||
|
|
||
| using namespace llvm::MachO; | ||
|
|
||
| namespace clang { | ||
| namespace installapi { | ||
|
|
||
| /// Metadata stored about a mapping of a declaration to a symbol. | ||
| struct DylibVerifier::SymbolContext { | ||
| // Name to use for printing in diagnostics. | ||
| std::string PrettyPrintName{""}; | ||
|
|
||
| // Name to use for all querying and verification | ||
| // purposes. | ||
| std::string SymbolName{""}; | ||
|
|
||
| // Kind to map symbol type against record. | ||
| EncodeKind Kind = EncodeKind::GlobalSymbol; | ||
|
|
||
| // Frontend Attributes tied to the AST. | ||
| const FrontendAttrs *FA = nullptr; | ||
|
|
||
| // The ObjCInterface symbol type, if applicable. | ||
| ObjCIFSymbolKind ObjCIFKind = ObjCIFSymbolKind::None; | ||
| }; | ||
|
|
||
| static std::string | ||
| getAnnotatedName(const Record *R, EncodeKind Kind, StringRef Name, | ||
| bool ValidSourceLoc = true, | ||
| ObjCIFSymbolKind ObjCIF = ObjCIFSymbolKind::None) { | ||
| assert(!Name.empty() && "Need symbol name for printing"); | ||
|
|
||
| std::string Annotation; | ||
| if (R->isWeakDefined()) | ||
| Annotation += "(weak-def) "; | ||
| if (R->isWeakReferenced()) | ||
| Annotation += "(weak-ref) "; | ||
| if (R->isThreadLocalValue()) | ||
| Annotation += "(tlv) "; | ||
|
|
||
| // Check if symbol represents only part of a @interface declaration. | ||
| const bool IsAnnotatedObjCClass = ((ObjCIF != ObjCIFSymbolKind::None) && | ||
| (ObjCIF <= ObjCIFSymbolKind::EHType)); | ||
|
|
||
| if (IsAnnotatedObjCClass) { | ||
| if (ObjCIF == ObjCIFSymbolKind::EHType) | ||
| Annotation += "Exception Type of "; | ||
| if (ObjCIF == ObjCIFSymbolKind::MetaClass) | ||
| Annotation += "Metaclass of "; | ||
| if (ObjCIF == ObjCIFSymbolKind::Class) | ||
| Annotation += "Class of "; | ||
| } | ||
|
|
||
| // Only print symbol type prefix or leading "_" if there is no source location | ||
| // tied to it. This can only ever happen when the location has to come from | ||
| // debug info. | ||
| if (ValidSourceLoc) { | ||
| if ((Kind == EncodeKind::GlobalSymbol) && Name.starts_with("_")) | ||
| return Annotation + Name.drop_front(1).str(); | ||
| return Annotation + Name.str(); | ||
| } | ||
|
|
||
| if (IsAnnotatedObjCClass) | ||
| return Annotation + Name.str(); | ||
|
|
||
| switch (Kind) { | ||
| case EncodeKind::GlobalSymbol: | ||
| return Annotation + Name.str(); | ||
| case EncodeKind::ObjectiveCInstanceVariable: | ||
| return Annotation + "(ObjC IVar) " + Name.str(); | ||
| case EncodeKind::ObjectiveCClass: | ||
| return Annotation + "(ObjC Class) " + Name.str(); | ||
| case EncodeKind::ObjectiveCClassEHType: | ||
| return Annotation + "(ObjC Class EH) " + Name.str(); | ||
| } | ||
|
|
||
| llvm_unreachable("unexpected case for EncodeKind"); | ||
| } | ||
|
|
||
| static std::string demangle(StringRef Name) { | ||
| // Itanium encoding requires 1 or 3 leading underscores, followed by 'Z'. | ||
| if (!(Name.starts_with("_Z") || Name.starts_with("___Z"))) | ||
| return Name.str(); | ||
| char *Result = llvm::itaniumDemangle(Name.data()); | ||
| if (!Result) | ||
| return Name.str(); | ||
|
|
||
| std::string Demangled(Result); | ||
| free(Result); | ||
| return Demangled; | ||
| } | ||
|
|
||
| static DylibVerifier::Result updateResult(const DylibVerifier::Result Prev, | ||
| const DylibVerifier::Result Curr) { | ||
| if (Prev == Curr) | ||
| return Prev; | ||
|
|
||
| // Never update from invalid or noverify state. | ||
| if ((Prev == DylibVerifier::Result::Invalid) || | ||
| (Prev == DylibVerifier::Result::NoVerify)) | ||
| return Prev; | ||
|
|
||
| // Don't let an ignored verification remove a valid one. | ||
| if (Prev == DylibVerifier::Result::Valid && | ||
| Curr == DylibVerifier::Result::Ignore) | ||
| return Prev; | ||
|
|
||
| return Curr; | ||
| } | ||
|
|
||
| void DylibVerifier::updateState(Result State) { | ||
| Ctx.FrontendState = updateResult(Ctx.FrontendState, State); | ||
| } | ||
|
|
||
| void DylibVerifier::addSymbol(const Record *R, SymbolContext &SymCtx, | ||
| TargetList &&Targets) { | ||
| if (Targets.empty()) | ||
| Targets = {Ctx.Target}; | ||
|
|
||
| Exports->addGlobal(SymCtx.Kind, SymCtx.SymbolName, R->getFlags(), Targets); | ||
| } | ||
|
|
||
| DylibVerifier::Result DylibVerifier::verifyImpl(Record *R, | ||
| SymbolContext &SymCtx) { | ||
| R->setVerify(); | ||
| if (!canVerify()) { | ||
| // Accumulate symbols when not in verifying against dylib. | ||
| if (R->isExported() && !SymCtx.FA->Avail.isUnconditionallyUnavailable() && | ||
| !SymCtx.FA->Avail.isObsoleted()) { | ||
| addSymbol(R, SymCtx); | ||
| } | ||
| return Ctx.FrontendState; | ||
| } | ||
| return Ctx.FrontendState; | ||
| } | ||
|
|
||
| bool DylibVerifier::canVerify() { | ||
| return Ctx.FrontendState != Result::NoVerify; | ||
| } | ||
|
|
||
| void DylibVerifier::setTarget(const Target &T) { | ||
| Ctx.Target = T; | ||
| Ctx.DiscoveredFirstError = false; | ||
| updateState(Dylib.empty() ? Result::NoVerify : Result::Ignore); | ||
| } | ||
|
|
||
| DylibVerifier::Result DylibVerifier::verify(ObjCIVarRecord *R, | ||
| const FrontendAttrs *FA, | ||
| const StringRef SuperClass) { | ||
| if (R->isVerified()) | ||
| return getState(); | ||
|
|
||
| std::string FullName = | ||
| ObjCIVarRecord::createScopedName(SuperClass, R->getName()); | ||
| SymbolContext SymCtx{ | ||
| getAnnotatedName(R, EncodeKind::ObjectiveCInstanceVariable, | ||
| Demangle ? demangle(FullName) : FullName), | ||
| FullName, EncodeKind::ObjectiveCInstanceVariable, FA}; | ||
| return verifyImpl(R, SymCtx); | ||
| } | ||
|
|
||
| static ObjCIFSymbolKind assignObjCIFSymbolKind(const ObjCInterfaceRecord *R) { | ||
| ObjCIFSymbolKind Result = ObjCIFSymbolKind::None; | ||
| if (R->getLinkageForSymbol(ObjCIFSymbolKind::Class) != RecordLinkage::Unknown) | ||
| Result |= ObjCIFSymbolKind::Class; | ||
| if (R->getLinkageForSymbol(ObjCIFSymbolKind::MetaClass) != | ||
| RecordLinkage::Unknown) | ||
| Result |= ObjCIFSymbolKind::MetaClass; | ||
| if (R->getLinkageForSymbol(ObjCIFSymbolKind::EHType) != | ||
| RecordLinkage::Unknown) | ||
| Result |= ObjCIFSymbolKind::EHType; | ||
| return Result; | ||
| } | ||
|
|
||
| DylibVerifier::Result DylibVerifier::verify(ObjCInterfaceRecord *R, | ||
| const FrontendAttrs *FA) { | ||
| if (R->isVerified()) | ||
| return getState(); | ||
| SymbolContext SymCtx; | ||
| SymCtx.SymbolName = R->getName(); | ||
| SymCtx.ObjCIFKind = assignObjCIFSymbolKind(R); | ||
|
|
||
| std::string DisplayName = | ||
| Demangle ? demangle(SymCtx.SymbolName) : SymCtx.SymbolName; | ||
| SymCtx.Kind = R->hasExceptionAttribute() ? EncodeKind::ObjectiveCClassEHType | ||
| : EncodeKind::ObjectiveCClass; | ||
| SymCtx.PrettyPrintName = getAnnotatedName(R, SymCtx.Kind, DisplayName); | ||
| SymCtx.FA = FA; | ||
|
|
||
| return verifyImpl(R, SymCtx); | ||
| } | ||
|
|
||
| DylibVerifier::Result DylibVerifier::verify(GlobalRecord *R, | ||
| const FrontendAttrs *FA) { | ||
| if (R->isVerified()) | ||
| return getState(); | ||
|
|
||
| // Global classifications could be obfusciated with `asm`. | ||
| SimpleSymbol Sym = parseSymbol(R->getName()); | ||
| SymbolContext SymCtx; | ||
| SymCtx.SymbolName = Sym.Name; | ||
| SymCtx.PrettyPrintName = | ||
| getAnnotatedName(R, Sym.Kind, Demangle ? demangle(Sym.Name) : Sym.Name); | ||
| SymCtx.Kind = Sym.Kind; | ||
| SymCtx.FA = FA; | ||
| return verifyImpl(R, SymCtx); | ||
| } | ||
|
|
||
| } // namespace installapi | ||
| } // namespace clang |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| // RUN: rm -rf %t | ||
| // RUN: split-file %s %t | ||
| // RUN: sed -e "s|DSTROOT|%/t|g" %t/inputs.json.in > %t/inputs.json | ||
|
|
||
| // RUN: clang-installapi -target arm64-apple-macos13.1 \ | ||
| // RUN: -I%t/usr/include \ | ||
| // RUN: -install_name @rpath/lib/libasm.dylib \ | ||
| // RUN: %t/inputs.json -o %t/output.tbd 2>&1 | FileCheck %s --allow-empty | ||
| // RUN: llvm-readtapi -compare %t/output.tbd %t/expected.tbd 2>&1 | FileCheck %s --allow-empty | ||
|
|
||
| // CHECK-NOT: error: | ||
| // CHECK-NOT: warning: | ||
|
|
||
| //--- usr/include/asm.h | ||
| #ifndef ASM_H | ||
| #define ASM_H | ||
|
|
||
| extern int ivar __asm("_OBJC_IVAR_$_SomeClass._ivar1"); | ||
| extern int objcClass1 __asm("_OBJC_CLASS_$_SomeClass"); | ||
| extern int objcClass2 __asm("_OBJC_METACLASS_$_SomeClass"); | ||
| extern int objcClass3 __asm("_OBJC_EHTYPE_$_SomeClass"); | ||
| extern int objcClass4 __asm(".objc_class_name_SomeClass"); | ||
|
|
||
| __attribute__((visibility("hidden"))) | ||
| @interface NSString { | ||
| } | ||
| @end | ||
|
|
||
| extern int ivarExtra __asm("_OBJC_IVAR_$_NSString._ivar1"); | ||
| #endif // ASM_H | ||
|
|
||
| //--- inputs.json.in | ||
| { | ||
| "headers": [ { | ||
| "path" : "DSTROOT/usr/include/asm.h", | ||
| "type" : "public" | ||
| }], | ||
| "version": "3" | ||
| } | ||
|
|
||
| //--- expected.tbd | ||
| { | ||
| "main_library": { | ||
| "compatibility_versions": [ | ||
| { | ||
| "version": "0" | ||
| } | ||
| ], | ||
| "current_versions": [ | ||
| { | ||
| "version": "0" | ||
| } | ||
| ], | ||
| "exported_symbols": [ | ||
| { | ||
| "data": { | ||
| "objc_class": [ | ||
| "SomeClass" | ||
| ], | ||
| "objc_eh_type": [ | ||
| "SomeClass" | ||
| ], | ||
| "objc_ivar": [ | ||
| "NSString._ivar1", | ||
| "SomeClass._ivar1" | ||
| ] | ||
| } | ||
| } | ||
| ], | ||
| "flags": [ | ||
| { | ||
| "attributes": [ | ||
| "not_app_extension_safe" | ||
| ] | ||
| } | ||
| ], | ||
| "install_names": [ | ||
| { | ||
| "name": "@rpath/lib/libasm.dylib" | ||
| } | ||
| ], | ||
| "target_info": [ | ||
| { | ||
| "min_deployment": "13.1", | ||
| "target": "arm64-macos" | ||
| } | ||
| ] | ||
| }, | ||
| "tapi_tbd_version": 5 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,9 @@ | ||
| /// Check non-darwin triple is rejected. | ||
| // RUN: not clang-installapi -target x86_64-unknown-unknown %s -o tmp.tbd 2> %t | ||
| // RUN: FileCheck --check-prefix INVALID_INSTALLAPI -input-file %t %s | ||
| // INVALID_INSTALLAPI: error: unsupported option 'installapi' for target 'x86_64-unknown-unknown' | ||
|
|
||
| /// Check that missing install_name is reported. | ||
| // RUN: not clang-installapi -target x86_64-apple-ios-simulator %s -o tmp.tbd 2> %t | ||
| // RUN: FileCheck --check-prefix INVALID_INSTALL_NAME -input-file %t %s | ||
| // INVALID_INSTALL_NAME: error: no install name specified: add -install_name <path> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| //===--- InstallAPIOpts.td ------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This file defines the specific options for InstallAPI. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // Include the common option parsing interfaces. | ||
| include "llvm/Option/OptParser.td" | ||
|
|
||
|
|
||
| ///////// | ||
| // Options | ||
|
|
||
| // TextAPI options. | ||
| def filetype : Joined<["--"], "filetype=">, | ||
| HelpText<"Specify the output file type (tbd-v4 or tbd-v5)">; | ||
|
|
||
| // Verification options. | ||
| def verify_against : Separate<["-"], "verify-against">, | ||
| HelpText<"Verify the specified dynamic library/framework against the headers">; | ||
| def verify_against_EQ : Joined<["--"], "verify-against=">, Alias<verify_against>; | ||
| def verify_mode_EQ : Joined<["--"], "verify-mode=">, | ||
| HelpText<"Specify the severity and extend of the validation. Valid modes are ErrorsOnly, ErrorsAndWarnings, and Pedantic.">; | ||
| def demangle : Flag<["--", "-"], "demangle">, | ||
| HelpText<"Demangle symbols when printing warnings and errors">; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| // -*- C++ -*- | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html | ||
|
|
||
| #ifndef _LIBCPP___CHRONO_LEAP_SECOND_H | ||
| #define _LIBCPP___CHRONO_LEAP_SECOND_H | ||
|
|
||
| #include <version> | ||
| // Enable the contents of the header only when libc++ was built with experimental features enabled. | ||
| #if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) | ||
|
|
||
| # include <__chrono/duration.h> | ||
| # include <__chrono/system_clock.h> | ||
| # include <__chrono/time_point.h> | ||
| # include <__compare/ordering.h> | ||
| # include <__compare/three_way_comparable.h> | ||
| # include <__config> | ||
|
|
||
| # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) | ||
| # pragma GCC system_header | ||
| # endif | ||
|
|
||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||
|
|
||
| # if _LIBCPP_STD_VER >= 20 | ||
|
|
||
| namespace chrono { | ||
|
|
||
| class leap_second { | ||
| public: | ||
| struct __constructor_tag; | ||
| [[nodiscard]] | ||
| _LIBCPP_HIDE_FROM_ABI explicit constexpr leap_second(__constructor_tag&&, sys_seconds __date, seconds __value) | ||
| : __date_(__date), __value_(__value) {} | ||
|
|
||
| _LIBCPP_HIDE_FROM_ABI leap_second(const leap_second&) = default; | ||
| _LIBCPP_HIDE_FROM_ABI leap_second& operator=(const leap_second&) = default; | ||
|
|
||
| _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr sys_seconds date() const noexcept { return __date_; } | ||
|
|
||
| _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr seconds value() const noexcept { return __value_; } | ||
|
|
||
| private: | ||
| sys_seconds __date_; | ||
| seconds __value_; | ||
| }; | ||
|
|
||
| _LIBCPP_HIDE_FROM_ABI inline constexpr bool operator==(const leap_second& __x, const leap_second& __y) { | ||
| return __x.date() == __y.date(); | ||
| } | ||
| _LIBCPP_HIDE_FROM_ABI inline constexpr strong_ordering operator<=>(const leap_second& __x, const leap_second& __y) { | ||
| return __x.date() <=> __y.date(); | ||
| } | ||
|
|
||
| template <class _Duration> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const leap_second& __x, const sys_time<_Duration>& __y) { | ||
| return __x.date() == __y; | ||
| } | ||
| template <class _Duration> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const leap_second& __x, const sys_time<_Duration>& __y) { | ||
| return __x.date() < __y; | ||
| } | ||
| template <class _Duration> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const sys_time<_Duration>& __x, const leap_second& __y) { | ||
| return __x < __y.date(); | ||
| } | ||
| template <class _Duration> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const leap_second& __x, const sys_time<_Duration>& __y) { | ||
| return __y < __x; | ||
| } | ||
| template <class _Duration> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const sys_time<_Duration>& __x, const leap_second& __y) { | ||
| return __y < __x; | ||
| } | ||
| template <class _Duration> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const leap_second& __x, const sys_time<_Duration>& __y) { | ||
| return !(__y < __x); | ||
| } | ||
| template <class _Duration> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const sys_time<_Duration>& __x, const leap_second& __y) { | ||
| return !(__y < __x); | ||
| } | ||
| template <class _Duration> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const leap_second& __x, const sys_time<_Duration>& __y) { | ||
| return !(__x < __y); | ||
| } | ||
| template <class _Duration> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const sys_time<_Duration>& __x, const leap_second& __y) { | ||
| return !(__x < __y); | ||
| } | ||
| template <class _Duration> | ||
| requires three_way_comparable_with<sys_seconds, sys_time<_Duration>> | ||
| _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const leap_second& __x, const sys_time<_Duration>& __y) { | ||
| return __x.date() <=> __y; | ||
| } | ||
|
|
||
| } // namespace chrono | ||
|
|
||
| # endif //_LIBCPP_STD_VER >= 20 | ||
|
|
||
| _LIBCPP_END_NAMESPACE_STD | ||
|
|
||
| #endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) | ||
|
|
||
| #endif // _LIBCPP___CHRONO_LEAP_SECOND_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // -*- C++ -*- | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html | ||
|
|
||
| #ifndef _LIBCPP_SRC_INCLUDE_TZDB_LEAP_SECOND_PRIVATE_H | ||
| #define _LIBCPP_SRC_INCLUDE_TZDB_LEAP_SECOND_PRIVATE_H | ||
|
|
||
| #include <chrono> | ||
|
|
||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||
|
|
||
| namespace chrono { | ||
|
|
||
| struct leap_second::__constructor_tag {}; | ||
|
|
||
| } // namespace chrono | ||
|
|
||
| _LIBCPP_END_NAMESPACE_STD | ||
|
|
||
| #endif // _LIBCPP_SRC_INCLUDE_TZDB_LEAP_SECOND_PRIVATE_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // UNSUPPORTED: c++03, c++11, c++14, c++17 | ||
| // UNSUPPORTED: no-filesystem, no-localization, no-tzdb | ||
|
|
||
| // XFAIL: libcpp-has-no-incomplete-tzdb | ||
| // XFAIL: availability-tzdb-missing | ||
|
|
||
| // <chrono> | ||
|
|
||
| // Tests the IANA database leap seconds parsing and operations. | ||
| // This is not part of the public tzdb interface. | ||
|
|
||
| #include <cassert> | ||
| #include <chrono> | ||
| #include <fstream> | ||
| #include <string> | ||
| #include <string_view> | ||
|
|
||
| #include "assert_macros.h" | ||
| #include "concat_macros.h" | ||
| #include "filesystem_test_helper.h" | ||
| #include "test_tzdb.h" | ||
|
|
||
| scoped_test_env env; | ||
| [[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo"); | ||
| const std::filesystem::path tzdata = env.create_file("zoneinfo/tzdata.zi"); | ||
| const std::filesystem::path leap_seconds = env.create_file("zoneinfo/leap-seconds.list"); | ||
|
|
||
| std::string_view std::chrono::__libcpp_tzdb_directory() { | ||
| static std::string result = dir.string(); | ||
| return result; | ||
| } | ||
|
|
||
| void write(std::string_view input) { | ||
| static int version = 0; | ||
|
|
||
| std::ofstream f{tzdata}; | ||
| f << "# version " << version++ << '\n'; | ||
| std::ofstream{leap_seconds}.write(input.data(), input.size()); | ||
| } | ||
|
|
||
| static const std::chrono::tzdb& parse(std::string_view input) { | ||
| write(input); | ||
| return std::chrono::reload_tzdb(); | ||
| } | ||
|
|
||
| static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) { | ||
| write(input); | ||
|
|
||
| TEST_VALIDATE_EXCEPTION( | ||
| std::runtime_error, | ||
| [&]([[maybe_unused]] const std::runtime_error& e) { | ||
| TEST_LIBCPP_REQUIRE( | ||
| e.what() == what, | ||
| TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n')); | ||
| }, | ||
| TEST_IGNORE_NODISCARD std::chrono::reload_tzdb()); | ||
| } | ||
|
|
||
| static void test_invalid() { | ||
| test_exception("0", "corrupt tzdb: expected a non-zero digit"); | ||
|
|
||
| test_exception("1", "corrupt tzdb: expected whitespace"); | ||
|
|
||
| test_exception("1 ", "corrupt tzdb: expected a non-zero digit"); | ||
| } | ||
|
|
||
| static void test_leap_seconds() { | ||
| using namespace std::chrono; | ||
|
|
||
| // Test whether loading also sorts the entries in the proper order. | ||
| const tzdb& result = parse( | ||
| R"( | ||
| 2303683200 12 # 1 Jan 1973 | ||
| 2287785600 11 # 1 Jul 1972 | ||
| 2272060800 10 # 1 Jan 1972 | ||
| 86400 1 # 2 Jan 1900 Dummy entry to test before 1970 | ||
| )"); | ||
|
|
||
| assert(result.leap_seconds.size() == 4); | ||
|
|
||
| assert(result.leap_seconds[0].date() == sys_seconds{sys_days{1900y / January / 2}}); | ||
| assert(result.leap_seconds[0].value() == 1s); | ||
|
|
||
| assert(result.leap_seconds[1].date() == sys_seconds{sys_days{1972y / January / 1}}); | ||
| assert(result.leap_seconds[1].value() == 10s); | ||
|
|
||
| assert(result.leap_seconds[2].date() == sys_seconds{sys_days{1972y / July / 1}}); | ||
| assert(result.leap_seconds[2].value() == 11s); | ||
|
|
||
| assert(result.leap_seconds[3].date() == sys_seconds{sys_days{1973y / January / 1}}); | ||
| assert(result.leap_seconds[3].value() == 12s); | ||
| } | ||
|
|
||
| int main(int, const char**) { | ||
| test_invalid(); | ||
| test_leap_seconds(); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // UNSUPPORTED: c++03, c++11, c++14, c++17 | ||
| // UNSUPPORTED: no-filesystem, no-localization, no-tzdb | ||
|
|
||
| // XFAIL: libcpp-has-no-incomplete-tzdb | ||
| // XFAIL: availability-tzdb-missing | ||
|
|
||
| // <chrono> | ||
|
|
||
| // Tests the loaded leap seconds match | ||
| // https://eel.is/c++draft/time.zone.leap.overview#2 | ||
| // | ||
| // At the moment of writing that list is the actual list. | ||
| // If in the future more leap seconds are added, the returned list may have more | ||
|
|
||
| #include <algorithm> | ||
| #include <array> | ||
| #include <cassert> | ||
| #include <chrono> | ||
| #include <ranges> | ||
|
|
||
| using namespace std::literals::chrono_literals; | ||
|
|
||
| // The list of leap seconds matching | ||
| // https://eel.is/c++draft/time.zone.leap.overview#2 | ||
| // At the moment of writing that list is the actual list in the IANA database. | ||
| // If in the future more leap seconds can be added. Testng th | ||
| static const std::array /*<std::chrono::leap_second>*/ leap_seconds = { | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1972y / std::chrono::January / 1}}, 10s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1972y / std::chrono::July / 1}}, 11s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1973y / std::chrono::January / 1}}, 12s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1974y / std::chrono::January / 1}}, 13s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1975y / std::chrono::January / 1}}, 14s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1976y / std::chrono::January / 1}}, 15s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1977y / std::chrono::January / 1}}, 16s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1978y / std::chrono::January / 1}}, 17s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1979y / std::chrono::January / 1}}, 18s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1980y / std::chrono::January / 1}}, 19s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1981y / std::chrono::July / 1}}, 20s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1982y / std::chrono::July / 1}}, 21s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1983y / std::chrono::July / 1}}, 22s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1985y / std::chrono::July / 1}}, 23s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1988y / std::chrono::January / 1}}, 24s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1990y / std::chrono::January / 1}}, 25s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1991y / std::chrono::January / 1}}, 26s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1992y / std::chrono::July / 1}}, 27s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1993y / std::chrono::July / 1}}, 28s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1994y / std::chrono::July / 1}}, 29s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1996y / std::chrono::January / 1}}, 30s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1997y / std::chrono::July / 1}}, 31s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1999y / std::chrono::January / 1}}, 32s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2006y / std::chrono::January / 1}}, 33s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2009y / std::chrono::January / 1}}, 34s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2012y / std::chrono::July / 1}}, 35s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2015y / std::chrono::July / 1}}, 36s), | ||
| std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2017y / std::chrono::January / 1}}, 37s)}; | ||
|
|
||
| int main(int, const char**) { | ||
| const std::chrono::tzdb& tzdb = std::chrono::get_tzdb(); | ||
|
|
||
| assert(tzdb.leap_seconds.size() >= leap_seconds.size()); | ||
| assert((std::ranges::equal( | ||
| leap_seconds, | ||
| tzdb.leap_seconds | std::ranges::views::take(leap_seconds.size()), | ||
| [](const auto& lhs, const auto& rhs) { return lhs.first == rhs.date() && lhs.second == rhs.value(); }))); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // UNSUPPORTED: c++03, c++11, c++14, c++17 | ||
| // UNSUPPORTED: no-filesystem, no-localization, no-tzdb | ||
|
|
||
| // XFAIL: libcpp-has-no-incomplete-tzdb | ||
| // XFAIL: availability-tzdb-missing | ||
|
|
||
| // <chrono> | ||
|
|
||
| // class leap_second | ||
| // { | ||
| // leap_second& operator=(const leap_second&) = default; | ||
| // | ||
| // ... | ||
| // }; | ||
|
|
||
| #include <chrono> | ||
| #include <memory> | ||
| #include <type_traits> | ||
| #include <cassert> | ||
|
|
||
| // Add the include path required by test_chrono_leap_second.h when using libc++. | ||
| // ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../src/include | ||
| #include "test_chrono_leap_second.h" | ||
|
|
||
| constexpr bool test() { | ||
| std::chrono::leap_second a = | ||
| test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{0}}, std::chrono::seconds{1}); | ||
| std::chrono::leap_second b = | ||
| test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{10}}, std::chrono::seconds{15}); | ||
|
|
||
| // operator== only compares the date member. | ||
| assert(a.date() != b.date()); | ||
| assert(a.value() != b.value()); | ||
|
|
||
| { | ||
| std::same_as<std::chrono::leap_second&> decltype(auto) result(b = a); | ||
| assert(std::addressof(result) == std::addressof(b)); | ||
|
|
||
| assert(a.date() == b.date()); | ||
| assert(a.value() == b.value()); | ||
| } | ||
|
|
||
| { | ||
| // Tests an rvalue uses the copy assignment. | ||
| std::same_as<std::chrono::leap_second&> decltype(auto) result(b = std::move(a)); | ||
| assert(std::addressof(result) == std::addressof(b)); | ||
|
|
||
| assert(a.date() == b.date()); | ||
| assert(a.value() == b.value()); | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| int main(int, const char**) { | ||
| static_assert(std::is_copy_assignable_v<std::chrono::leap_second>); | ||
|
|
||
| test(); | ||
| static_assert(test()); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // UNSUPPORTED: c++03, c++11, c++14, c++17 | ||
| // UNSUPPORTED: no-filesystem, no-localization, no-tzdb | ||
|
|
||
| // XFAIL: libcpp-has-no-incomplete-tzdb | ||
| // XFAIL: availability-tzdb-missing | ||
|
|
||
| // <chrono> | ||
|
|
||
| // class leap_second | ||
| // { | ||
| // leap_second(const leap_second&) = default; | ||
| // | ||
| // ... | ||
| // }; | ||
|
|
||
| #include <chrono> | ||
| #include <concepts> | ||
| #include <cassert> | ||
|
|
||
| // Add the include path required by test_chrono_leap_second.h when using libc++. | ||
| // ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../src/include | ||
| #include "test_chrono_leap_second.h" | ||
|
|
||
| constexpr bool test() { | ||
| std::chrono::leap_second a = | ||
| test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{0}}, std::chrono::seconds{1}); | ||
|
|
||
| { | ||
| std::chrono::leap_second b = a; | ||
|
|
||
| // operator== only compares the date member. | ||
| assert(a.date() == b.date()); | ||
| assert(a.value() == b.value()); | ||
| } | ||
|
|
||
| #ifdef _LIBCPP_VERSION | ||
| { | ||
| // Tests an rvalue uses the copy constructor. | ||
| // Since implementations are allowed to add additional constructors this is | ||
| // a libc++ specific test. | ||
| std::chrono::leap_second b = std::move(a); | ||
|
|
||
| // operator== only compares the date member. | ||
| assert(a.date() == b.date()); | ||
| assert(a.value() == b.value()); | ||
| } | ||
| #endif // _LIBCPP_VERSION | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| int main(int, const char**) { | ||
| static_assert(std::copy_constructible<std::chrono::leap_second>); | ||
|
|
||
| test(); | ||
| static_assert(test()); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // UNSUPPORTED: c++03, c++11, c++14, c++17 | ||
| // UNSUPPORTED: no-filesystem, no-localization, no-tzdb | ||
|
|
||
| // XFAIL: libcpp-has-no-incomplete-tzdb | ||
| // XFAIL: availability-tzdb-missing | ||
|
|
||
| // <chrono> | ||
|
|
||
| // class leap_second; | ||
|
|
||
| // constexpr sys_seconds date() const noexcept; | ||
|
|
||
| #include <cassert> | ||
| #include <chrono> | ||
|
|
||
| #include "test_macros.h" | ||
|
|
||
| // Add the include path required by test_chrono_leap_second.h when using libc++. | ||
| // ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../../src/include | ||
| #include "test_chrono_leap_second.h" | ||
|
|
||
| constexpr void test(const std::chrono::leap_second leap_second, std::chrono::sys_seconds expected) { | ||
| std::same_as<std::chrono::sys_seconds> auto date = leap_second.date(); | ||
| assert(date == expected); | ||
| static_assert(noexcept(leap_second.date())); | ||
| } | ||
|
|
||
| constexpr bool test() { | ||
| test(test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{0}}, std::chrono::seconds{1}), | ||
| std::chrono::sys_seconds{std::chrono::seconds{0}}); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| int main(int, const char**) { | ||
| test(); | ||
| static_assert(test()); | ||
|
|
||
| // test with the real tzdb | ||
| const std::chrono::tzdb& tzdb = std::chrono::get_tzdb(); | ||
| assert(!tzdb.leap_seconds.empty()); | ||
| test(tzdb.leap_seconds[0], tzdb.leap_seconds[0].date()); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // UNSUPPORTED: c++03, c++11, c++14, c++17 | ||
| // UNSUPPORTED: no-filesystem, no-localization, no-tzdb | ||
|
|
||
| // XFAIL: libcpp-has-no-incomplete-tzdb | ||
| // XFAIL: availability-tzdb-missing | ||
|
|
||
| // <chrono> | ||
|
|
||
| // class leap_second; | ||
|
|
||
| // constexpr seconds value() const noexcept; | ||
|
|
||
| #include <cassert> | ||
| #include <chrono> | ||
|
|
||
| #include "test_macros.h" | ||
|
|
||
| // Add the include path required by test_chrono_leap_second.h when using libc++. | ||
| // ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../../src/include | ||
| #include "test_chrono_leap_second.h" | ||
|
|
||
| constexpr void test(const std::chrono::leap_second leap_second, std::chrono::seconds expected) { | ||
| std::same_as<std::chrono::seconds> auto value = leap_second.value(); | ||
| assert(value == expected); | ||
| static_assert(noexcept(leap_second.value())); | ||
| } | ||
|
|
||
| constexpr bool test() { | ||
| test(test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{0}}, std::chrono::seconds{1}), | ||
| std::chrono::seconds{1}); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| int main(int, const char**) { | ||
| test(); | ||
| static_assert(test()); | ||
|
|
||
| // test with the real tzdb | ||
| const std::chrono::tzdb& tzdb = std::chrono::get_tzdb(); | ||
| assert(!tzdb.leap_seconds.empty()); | ||
| test(tzdb.leap_seconds[0], tzdb.leap_seconds[0].value()); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // UNSUPPORTED: c++03, c++11, c++14, c++17 | ||
| // UNSUPPORTED: no-filesystem, no-localization, no-tzdb | ||
|
|
||
| // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME | ||
|
|
||
| // XFAIL: libcpp-has-no-incomplete-tzdb | ||
| // XFAIL: availability-tzdb-missing | ||
|
|
||
| // <chrono> | ||
|
|
||
| // class leap_second; | ||
|
|
||
| //constexpr bool operator==(const leap_second& x, const leap_second& y); // C++20 | ||
| //constexpr strong_ordering operator<=>(const leap_second& x, const leap_second& y); | ||
| // | ||
| //template<class Duration> | ||
| // constexpr bool operator==(const leap_second& x, const sys_time<Duration>& y); | ||
| //template<class Duration> | ||
| // constexpr bool operator< (const leap_second& x, const sys_time<Duration>& y); | ||
| //template<class Duration> | ||
| // constexpr bool operator< (const sys_time<Duration>& x, const leap_second& y); | ||
| //template<class Duration> | ||
| // constexpr bool operator> (const leap_second& x, const sys_time<Duration>& y); | ||
| //template<class Duration> | ||
| // constexpr bool operator> (const sys_time<Duration>& x, const leap_second& y); | ||
| //template<class Duration> | ||
| // constexpr bool operator<=(const leap_second& x, const sys_time<Duration>& y); | ||
| //template<class Duration> | ||
| // constexpr bool operator<=(const sys_time<Duration>& x, const leap_second& y); | ||
| //template<class Duration> | ||
| // constexpr bool operator>=(const leap_second& x, const sys_time<Duration>& y); | ||
| //template<class Duration> | ||
| // constexpr bool operator>=(const sys_time<Duration>& x, const leap_second& y); | ||
| //template<class Duration> | ||
| // requires three_way_comparable_with<sys_seconds, sys_time<Duration>> | ||
| // constexpr auto operator<=>(const leap_second& x, const sys_time<Duration>& y); | ||
|
|
||
| #include <cassert> | ||
| #include <chrono> | ||
|
|
||
| #include "test_macros.h" | ||
| #include "test_comparisons.h" | ||
|
|
||
| // Add the include path required by test_chrono_leap_second.h when using libc++. | ||
| // ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../../src/include | ||
| #include "test_chrono_leap_second.h" | ||
|
|
||
| constexpr void test(const std::chrono::leap_second lhs, const std::chrono::leap_second rhs) { | ||
| AssertOrderReturn<std::strong_ordering, std::chrono::leap_second>(); | ||
| assert(testOrder(lhs, rhs, std::strong_ordering::less)); | ||
|
|
||
| AssertOrderReturn<std::strong_ordering, std::chrono::leap_second, std::chrono::sys_seconds>(); | ||
| assert(testOrder(lhs, rhs.date(), std::strong_ordering::less)); | ||
|
|
||
| AssertOrderReturn<std::strong_ordering, std::chrono::sys_seconds, std::chrono::leap_second>(); | ||
| assert(testOrder(lhs.date(), rhs, std::strong_ordering::less)); | ||
| } | ||
|
|
||
| constexpr bool test() { | ||
| test(test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{0}}, std::chrono::seconds{1}), | ||
| test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{1}}, std::chrono::seconds{2})); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| int main(int, const char**) { | ||
| test(); | ||
| static_assert(test()); | ||
|
|
||
| // test with the real tzdb | ||
| const std::chrono::tzdb& tzdb = std::chrono::get_tzdb(); | ||
| assert(tzdb.leap_seconds.size() > 2); | ||
| test(tzdb.leap_seconds[0], tzdb.leap_seconds[1]); | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // -*- C++ -*- | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef SUPPORT_TEST_CHRONO_LEAP_SECOND_HPP | ||
| #define SUPPORT_TEST_CHRONO_LEAP_SECOND_HPP | ||
|
|
||
| // Contains helper functions to create a std::chrono::leap_second. | ||
| // | ||
| // Since the standard doesn't specify how a @ref std::chrono::leap_second is | ||
| // constructed this is implementation defined. To make the public API tests of | ||
| // the class generic this header defines helper functions to create the | ||
| // required object. | ||
| // | ||
| // Note This requires every standard library implementation to write their own | ||
| // helper function. Vendors are encouraged to create a pull request at | ||
| // https://github.com/llvm/llvm-project so their specific implementation can be | ||
| // part of this file. | ||
|
|
||
| #include "test_macros.h" | ||
|
|
||
| #if TEST_STD_VER < 20 | ||
| # error "The format header requires at least C++20" | ||
| #endif | ||
|
|
||
| #include <chrono> | ||
|
|
||
| #ifdef _LIBCPP_VERSION | ||
|
|
||
| // In order to find this include the calling test needs to provide this path in | ||
| // the search path. Typically this looks like: | ||
| // ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../../src/include | ||
| // where the number of `../` sequences depends on the subdirectory level of the | ||
| // test. | ||
| # include "tzdb/leap_second_private.h" // Header in the dylib | ||
|
|
||
| inline constexpr std::chrono::leap_second | ||
| test_leap_second_create(const std::chrono::sys_seconds& date, const std::chrono::seconds& value) { | ||
| return std::chrono::leap_second{std::chrono::leap_second::__constructor_tag{}, date, value}; | ||
| } | ||
|
|
||
| #else // _LIBCPP_VERSION | ||
| # error \ | ||
| "Please create a vendor specific version of the test typedef and file a PR at https://github.com/llvm/llvm-project" | ||
| #endif // _LIBCPP_VERSION | ||
|
|
||
| #endif // SUPPORT_TEST_CHRONO_LEAP_SECOND_HPP |