diff --git a/Dockerfiles/alpine b/Dockerfiles/alpine index c0e032ff..03836aaa 100644 --- a/Dockerfiles/alpine +++ b/Dockerfiles/alpine @@ -2,4 +2,4 @@ FROM alpine:latest -RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon +RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon tzdata diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 9f950192..ddcdf609 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -1,21 +1,14 @@ #pragma once -#include -#if FMT_VERSION < 60000 -#include -#else -#include -#endif #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" -namespace waybar::modules { +namespace waybar { -struct waybar_time { - std::locale locale; - date::zoned_seconds ztime; -}; +struct waybar_time; + +namespace modules { const std::string kCalendarPlaceholder = "calendar"; @@ -43,4 +36,5 @@ class Clock : public ALabel { bool is_timezone_fixed(); }; -} // namespace waybar::modules +} // namespace modules +} // namespace waybar diff --git a/include/util/waybar_time.hpp b/include/util/waybar_time.hpp new file mode 100644 index 00000000..c74e58c3 --- /dev/null +++ b/include/util/waybar_time.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +namespace waybar { + +struct waybar_time { + std::locale locale; + date::zoned_seconds ztime; +}; + +} // namespace waybar + +template <> +struct fmt::formatter { + std::string_view specs; + + template + constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + if (it != ctx.end() && *it == ':') { + ++it; + } + auto end = it; + while (end != ctx.end() && *end != '}') { + ++end; + } + if (end != it) { + specs = {it, std::string_view::size_type(end - it)}; + } + return end; + } + + template + auto format(const waybar::waybar_time& t, FormatContext& ctx) { + return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(specs), t.ztime)); + } +}; diff --git a/meson.build b/meson.build index 2c434266..1ccf6340 100644 --- a/meson.build +++ b/meson.build @@ -79,7 +79,7 @@ is_netbsd = host_machine.system() == 'netbsd' is_openbsd = host_machine.system() == 'openbsd' thread_dep = dependency('threads') -fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep']) +fmt = dependency('fmt', version : ['>=7.0.0'], fallback : ['fmt', 'fmt_dep']) spdlog = dependency('spdlog', version : ['>=1.8.5'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 7e7d7420..b0a6776a 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,17 +1,24 @@ #include "modules/clock.hpp" -#include #include +#if FMT_VERSION < 60000 +#include +#else +#include +#endif +#include #include #include + #include "util/ustring_clen.hpp" +#include "util/waybar_time.hpp" #ifdef HAVE_LANGINFO_1STDAY #include #include #endif -using waybar::modules::waybar_time; +using waybar::waybar_time; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), @@ -227,14 +234,3 @@ auto waybar::modules::Clock::first_day_of_week() -> date::weekday { #endif return date::Sunday; } - -template <> -struct fmt::formatter : fmt::formatter { - template - auto format(const waybar_time& t, FormatContext& ctx) { -#if FMT_VERSION >= 80000 - auto& tm_format = specs; -#endif - return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime)); - } -}; diff --git a/test/SafeSignal.cpp b/test/SafeSignal.cpp index 2c67317b..244487f2 100644 --- a/test/SafeSignal.cpp +++ b/test/SafeSignal.cpp @@ -1,4 +1,3 @@ -#define CATCH_CONFIG_RUNNER #include "util/SafeSignal.hpp" #include @@ -138,8 +137,3 @@ TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal copy/move counter", "[signal][thr producer.join(); REQUIRE(count == NUM_EVENTS); } - -int main(int argc, char* argv[]) { - Glib::init(); - return Catch::Session().run(argc, argv); -} diff --git a/test/config.cpp b/test/config.cpp index edd6d6b8..29b0502a 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -1,4 +1,3 @@ -#define CATCH_CONFIG_MAIN #include "config.hpp" #include diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 00000000..bbfcc9f2 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,26 @@ +#define CATCH_CONFIG_RUNNER +#include +#include +#include + +#include +#include +#include + +int main(int argc, char* argv[]) { + Catch::Session session; + Glib::init(); + + session.applyCommandLine(argc, argv); + const auto logger = spdlog::default_logger(); + const auto& reporter_name = session.config().getReporterName(); + if (reporter_name == "tap") { + spdlog::set_pattern("# [%l] %v"); + } else if (reporter_name == "compact") { + logger->sinks().clear(); + } else { + logger->sinks().assign({std::make_shared()}); + } + + return session.run(); +} diff --git a/test/meson.build b/test/meson.build index bbef21e7..b1e11237 100644 --- a/test/meson.build +++ b/test/meson.build @@ -6,30 +6,27 @@ test_dep = [ jsoncpp, spdlog, ] - -config_test = executable( - 'config_test', +test_src = files( + 'main.cpp', + 'SafeSignal.cpp', 'config.cpp', '../src/config.cpp', - dependencies: test_dep, - include_directories: test_inc, ) -safesignal_test = executable( - 'safesignal_test', - 'SafeSignal.cpp', +if tz_dep.found() + test_dep += tz_dep + test_src += files('waybar_time.cpp') +endif + +waybar_test = executable( + 'waybar_test', + test_src, dependencies: test_dep, include_directories: test_inc, ) test( - 'Configuration test', - config_test, - workdir: meson.source_root(), -) - -test( - 'SafeSignal test', - safesignal_test, + 'waybar', + waybar_test, workdir: meson.source_root(), ) diff --git a/test/waybar_time.cpp b/test/waybar_time.cpp new file mode 100644 index 00000000..5fc3312d --- /dev/null +++ b/test/waybar_time.cpp @@ -0,0 +1,90 @@ +#include "util/waybar_time.hpp" + +#include +#include + +#include +#include +#include + +using namespace std::literals::chrono_literals; + +/* + * Check that the date/time formatter with locale and timezone support is working as expected. + */ + +const date::zoned_time TEST_TIME = date::make_zoned( + "UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s); + +TEST_CASE("Format UTC time", "[clock][util]") { + waybar::waybar_time tm{std::locale("C"), TEST_TIME}; + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + + /* Test a few locales that are most likely to be present */ + SECTION("US locale") { + try { + tm.locale = std::locale("en_US"); + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM")); + REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } + SECTION("GB locale") { + try { + tm.locale = std::locale("en_GB"); + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05")); + REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 13:04:05"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } +} + +TEST_CASE("Format zoned time", "[clock][util]") { + waybar::waybar_time tm{std::locale("C"), date::make_zoned("America/New_York", TEST_TIME)}; + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + + /* Test a few locales that are most likely to be present */ + SECTION("US locale") { + try { + tm.locale = std::locale("en_US"); + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM")); + REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } + + SECTION("GB locale") { + try { + tm.locale = std::locale("en_GB"); + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05")); + REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 08:04:05"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } +}