Skip to content

Commit 1d6a056

Browse files
Chris Townsendsharder996
Chris Townsend
andauthored
Merge #3237
3237: [snapshots] use user locale in timestamp output r=ricab a=sharder996 Co-authored-by: sharder996 <[email protected]>
2 parents 8098db7 + 4a1b2dc commit 1d6a056

8 files changed

+81
-6
lines changed

include/multipass/cli/format_utils.h

+10
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include <algorithm>
2929
#include <string>
3030

31+
#define MP_FORMAT_UTILS multipass::FormatUtils::instance()
32+
3133
namespace multipass
3234
{
3335
class Formatter;
@@ -58,6 +60,14 @@ static constexpr auto column_width =
5860
return std::max({get_width(*max_width) + col_buffer, header_width + col_buffer, minimum_width});
5961
};
6062
} // namespace format
63+
64+
class FormatUtils : public Singleton<FormatUtils>
65+
{
66+
public:
67+
FormatUtils(const Singleton<FormatUtils>::PrivatePass&) noexcept;
68+
69+
virtual std::string convert_to_user_locale(const google::protobuf::Timestamp& timestamp) const;
70+
};
6171
} // namespace multipass
6272

6373
template <typename Container>

src/client/cli/formatter/csv_formatter.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ std::string generate_snapshot_details(const mp::InfoReply reply)
8585

8686
fmt::format_to(std::back_inserter(buf),
8787
",{},{},{},\"{}\"\n",
88-
google::protobuf::util::TimeUtil::ToString(fundamentals.creation_timestamp()),
88+
MP_FORMAT_UTILS.convert_to_user_locale(fundamentals.creation_timestamp()),
8989
fundamentals.parent(),
9090
fmt::join(info.snapshot_info().children(), ";"),
9191
fundamentals.comment());

src/client/cli/formatter/format_utils.cpp

+20-2
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@
1515
*
1616
*/
1717

18+
#include <multipass/cli/csv_formatter.h>
1819
#include <multipass/cli/format_utils.h>
1920
#include <multipass/cli/formatter.h>
20-
21-
#include <multipass/cli/csv_formatter.h>
2221
#include <multipass/cli/json_formatter.h>
2322
#include <multipass/cli/table_formatter.h>
2423
#include <multipass/cli/yaml_formatter.h>
2524

25+
#include <locale>
26+
#include <sstream>
27+
2628
namespace mp = multipass;
2729

2830
namespace
@@ -110,3 +112,19 @@ void mp::format::filter_aliases(google::protobuf::RepeatedPtrField<multipass::Fi
110112
aliases.DeleteSubrange(i, 1);
111113
}
112114
}
115+
116+
mp::FormatUtils::FormatUtils(const Singleton<FormatUtils>::PrivatePass& pass) noexcept
117+
: Singleton<FormatUtils>::Singleton{pass}
118+
{
119+
}
120+
121+
std::string mp::FormatUtils::convert_to_user_locale(const google::protobuf::Timestamp& timestamp) const
122+
{
123+
std::ostringstream oss;
124+
oss.imbue(std::locale(""));
125+
126+
std::time_t t = google::protobuf::util::TimeUtil::TimestampToTimeT(timestamp);
127+
oss << std::put_time(std::localtime(&t), "%c %Z");
128+
129+
return oss.str();
130+
}

src/client/cli/formatter/json_formatter.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ QJsonObject generate_snapshot_details(const mp::DetailedInfoItem& item)
7878

7979
snapshot_info.insert(
8080
"created",
81-
QString::fromStdString(google::protobuf::util::TimeUtil::ToString(fundamentals.creation_timestamp())));
81+
QString::fromStdString(MP_FORMAT_UTILS.convert_to_user_locale(fundamentals.creation_timestamp())));
8282
snapshot_info.insert("parent", QString::fromStdString(fundamentals.parent()));
8383

8484
QJsonArray children;

src/client/cli/formatter/table_formatter.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ std::string generate_snapshot_details(const mp::DetailedInfoItem& item)
8888
fmt::format_to(std::back_inserter(buf),
8989
"{:<16}{}\n",
9090
"Created:",
91-
google::protobuf::util::TimeUtil::ToString(fundamentals.creation_timestamp()));
91+
MP_FORMAT_UTILS.convert_to_user_locale(fundamentals.creation_timestamp()));
9292
fmt::format_to(std::back_inserter(buf),
9393
"{:<16}{}\n",
9494
"Parent:",

src/client/cli/formatter/yaml_formatter.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ YAML::Node generate_snapshot_details(const mp::DetailedInfoItem& item)
7878
}
7979
snapshot_node["mounts"] = mounts;
8080

81-
snapshot_node["created"] = google::protobuf::util::TimeUtil::ToString(fundamentals.creation_timestamp());
81+
snapshot_node["created"] = MP_FORMAT_UTILS.convert_to_user_locale(fundamentals.creation_timestamp());
8282
snapshot_node["parent"] = fundamentals.parent().empty() ? YAML::Node() : YAML::Node(fundamentals.parent());
8383

8484
snapshot_node["children"] = YAML::Node(YAML::NodeType::Sequence);

tests/mock_format_utils.h

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (C) Canonical, Ltd.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; version 3.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License
14+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
*
16+
*/
17+
18+
#ifndef MULTIPASS_MOCK_FORMAT_UTILS_H
19+
#define MULTIPASS_MOCK_FORMAT_UTILS_H
20+
21+
#include "mock_singleton_helpers.h"
22+
23+
#include <multipass/cli/format_utils.h>
24+
25+
namespace multipass::test
26+
{
27+
class MockFormatUtils : public FormatUtils
28+
{
29+
public:
30+
using FormatUtils::FormatUtils;
31+
32+
MOCK_METHOD(std::string, convert_to_user_locale, (const google::protobuf::Timestamp&), (const, override));
33+
34+
MP_MOCK_SINGLETON_BOILERPLATE(MockFormatUtils, FormatUtils);
35+
};
36+
} // namespace multipass::test
37+
38+
#endif // MULTIPASS_MOCK_FORMAT_UTILS_H

tests/test_output_formatter.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
#include "common.h"
19+
#include "mock_format_utils.h"
1920
#include "mock_settings.h"
2021

2122
#include <multipass/cli/csv_formatter.h>
@@ -748,6 +749,11 @@ class BaseFormatterSuite : public testing::Test
748749
// The tests expected output are for the default C locale
749750
std::locale::global(std::locale("C"));
750751
EXPECT_CALL(mock_settings, get(Eq(mp::petenv_key))).WillRepeatedly(Return("pet"));
752+
753+
// Timestamps in tests need to be in a consistent locale
754+
EXPECT_CALL(mock_format_utils, convert_to_user_locale(_)).WillRepeatedly([](const auto timestamp) {
755+
return google::protobuf::util::TimeUtil::ToString(timestamp);
756+
});
751757
}
752758

753759
~BaseFormatterSuite()
@@ -759,6 +765,9 @@ class BaseFormatterSuite : public testing::Test
759765
mpt::MockSettings::GuardedMock mock_settings_injection = mpt::MockSettings::inject<StrictMock>();
760766
mpt::MockSettings& mock_settings = *mock_settings_injection.first;
761767

768+
mpt::MockFormatUtils::GuardedMock mock_format_utils_injection = mpt::MockFormatUtils::inject<NiceMock>();
769+
mpt::MockFormatUtils& mock_format_utils = *mock_format_utils_injection.first;
770+
762771
private:
763772
std::locale saved_locale;
764773
};

0 commit comments

Comments
 (0)