Skip to content

Commit 5fc301c

Browse files
authored
Make MacStatsConfigUtilImpl stateless (#1496)
This is a preparation step for removing the Singleton<T> dependency in config/stats_config_util.cc. The two member fields of MacStatsConfigUtilImpl were process-global state expressed as instance state (only one instance has ever existed, courtesy of Singleton<T>): * config_file_ cached "${user_profile_dir}/.usagestats.db". SystemUtil::GetUserProfileDirectory() is already called from many places, so caching its result inside this one class does not buy much. If profiling shows the call is hot, the right fix is to memoize it inside SystemUtil so every caller benefits. * "mutex_" serialized the multi-step file read/write sequence in "SetEnabled" against concurrent callers. Process-global protection is what we have had so far, so it is now a "constinit absl::Mutex" at namespace scope, matching the established pattern in "base/android_util.cc" and "renderer/win32/win32_renderer_client.cc". The class now holds no state, which makes the subsequent "Singleton<T>" removal mechanical. There is no observable behavioral change in production. Note that we have had zero test coverage of MacStatsConfigUtilImpl for a long time. Filling the gap has been actually difficult in practice because a naive test would write to "${HOME}/.usagestats.db" and chmod it, clobbering the developer's real opt-in/out state. Now that "SystemUtil::SetUserProfileDirectory()" takes effect immediately inside the implementation, the standard "TestWithTempUserProfile" fixture is sufficient to redirect the file into a per-test temp dir. The new tests cover both branches of the CHANNEL_DEV switch for MacStatsConfigUtilImpl: * Stable (!CHANNEL_DEV): default-false when no file exists, the set/get round-trip for both "true" and "false", and that "SetEnabled" can overwrite a previously-written value. The last one is load-bearing because "SetEnabled" chmods the file to read-only after writing, so a subsequent "SetEnabled" must chmod it back to writable before truncating; this test exercises that round-trip. * Dev channel: "IsEnabled" returns "true" regardless of file state, and "SetEnabled" is a no-op that never creates the file on disk. PiperOrigin-RevId: 913010829
1 parent b940437 commit 5fc301c

3 files changed

Lines changed: 90 additions & 18 deletions

File tree

src/config/BUILD.bazel

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ mozc_cc_test(
136136
"//testing:mozctest",
137137
"//protocol:config_cc_proto",
138138
],
139+
macos = [
140+
"//base:file_util",
141+
"//base:system_util",
142+
"//testing:mozctest",
143+
"@com_google_absl//absl/strings",
144+
],
139145
windows = [
140146
"//base:bits",
141147
"//base/win32:win_api_test_helper",

src/config/stats_config_util.cc

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -149,31 +149,28 @@ bool WinStatsConfigUtilImpl::SetEnabled(bool val) {
149149
#endif // _WIN32
150150

151151
#ifdef __APPLE__
152+
constinit absl::Mutex g_mutex(absl::kConstInit);
153+
154+
std::string GetConfigFilePath() {
155+
return absl::StrCat(SystemUtil::GetUserProfileDirectory(),
156+
"/.usagestats.db"); // hidden file
157+
}
158+
152159
class MacStatsConfigUtilImpl : public StatsConfigUtilInterface {
153160
public:
154-
MacStatsConfigUtilImpl();
155-
156161
bool IsEnabled() override;
157162
bool SetEnabled(bool val) override;
158-
159-
private:
160-
std::string config_file_;
161-
absl::Mutex mutex_;
162163
};
163164

164-
MacStatsConfigUtilImpl::MacStatsConfigUtilImpl()
165-
: config_file_(absl::StrCat(SystemUtil::GetUserProfileDirectory(),
166-
"/.usagestats.db")) // // hidden file
167-
{}
168-
169165
bool MacStatsConfigUtilImpl::IsEnabled() {
170166
#ifdef CHANNEL_DEV
171167
return true;
172168
#else // CHANNEL_DEV
173-
absl::MutexLock l(&mutex_);
169+
absl::MutexLock l(g_mutex);
174170
constexpr bool kDefaultValue = false;
175171

176-
std::ifstream ifs(config_file_.c_str(), std::ios::binary | std::ios::in);
172+
const std::string config_file = GetConfigFilePath();
173+
std::ifstream ifs(config_file.c_str(), std::ios::binary | std::ios::in);
177174

178175
if (!ifs.is_open()) {
179176
return kDefaultValue;
@@ -197,13 +194,14 @@ bool MacStatsConfigUtilImpl::SetEnabled(bool val) {
197194
#ifdef CHANNEL_DEV
198195
return true;
199196
#else // CHANNEL_DEV
200-
absl::MutexLock l(&mutex_);
197+
absl::MutexLock l(g_mutex);
201198
const uint32_t value = static_cast<uint32_t>(val);
202199

203-
if (FileUtil::FileExists(config_file_).ok()) {
204-
::chmod(config_file_.c_str(), S_IRUSR | S_IWUSR); // read/write
200+
const std::string config_file = GetConfigFilePath();
201+
if (FileUtil::FileExists(config_file).ok()) {
202+
::chmod(config_file.c_str(), S_IRUSR | S_IWUSR); // read/write
205203
}
206-
std::ofstream ofs(config_file_,
204+
std::ofstream ofs(config_file,
207205
std::ios::binary | std::ios::out | std::ios::trunc);
208206
if (!ofs) {
209207
return false;
@@ -212,7 +210,7 @@ bool MacStatsConfigUtilImpl::SetEnabled(bool val) {
212210
if (!ofs.good()) {
213211
return false;
214212
}
215-
return 0 == ::chmod(config_file_.c_str(), S_IRUSR); // read only
213+
return 0 == ::chmod(config_file.c_str(), S_IRUSR); // read only
216214
#endif // CHANNEL_DEV
217215
}
218216
#endif // MACOSX

src/config/stats_config_util_test.cc

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@
3838
#include "testing/mozctest.h"
3939
#endif // __ANDROID__
4040

41+
#ifdef __APPLE__
42+
#include <TargetConditionals.h>
43+
#if TARGET_OS_OSX
44+
#include <string>
45+
46+
#include "absl/strings/str_cat.h"
47+
#include "base/file_util.h"
48+
#include "base/system_util.h"
49+
#include "testing/mozctest.h"
50+
#endif // TARGET_OS_OSX
51+
#endif // __APPLE__
52+
4153
#ifdef _WIN32
4254
#include <bit>
4355

@@ -674,6 +686,62 @@ TEST(StatsConfigUtilTestWin, IsEnabled) {
674686
#endif // !CHANNEL_DEV
675687
#endif // _WIN32
676688

689+
#ifdef TARGET_OS_OSX
690+
class StatsConfigUtilTestMac : public ::mozc::testing::TestWithTempUserProfile {
691+
protected:
692+
std::string ConfigFilePath() const {
693+
return absl::StrCat(SystemUtil::GetUserProfileDirectory(),
694+
"/.usagestats.db");
695+
}
696+
};
697+
698+
#ifdef CHANNEL_DEV
699+
TEST_F(StatsConfigUtilTestMac, IsEnabledIsAlwaysTrueInDevChannel) {
700+
// In dev channel, IsEnabled always returns true regardless of the file's
701+
// state.
702+
EXPECT_TRUE(StatsConfigUtil::IsEnabled());
703+
// SetEnabled is also a no-op that always returns true.
704+
EXPECT_TRUE(StatsConfigUtil::SetEnabled(false));
705+
EXPECT_TRUE(StatsConfigUtil::IsEnabled());
706+
}
707+
708+
TEST_F(StatsConfigUtilTestMac, SetEnabledNeverWritesFileInDevChannel) {
709+
// In dev channel, SetEnabled must not touch the on-disk config file.
710+
EXPECT_TRUE(StatsConfigUtil::SetEnabled(true));
711+
EXPECT_FALSE(FileUtil::FileExists(ConfigFilePath()).ok());
712+
EXPECT_TRUE(StatsConfigUtil::SetEnabled(false));
713+
EXPECT_FALSE(FileUtil::FileExists(ConfigFilePath()).ok());
714+
}
715+
#else // !CHANNEL_DEV
716+
TEST_F(StatsConfigUtilTestMac, IsEnabledDefaultsToFalse) {
717+
// Without a config file, IsEnabled returns false.
718+
ASSERT_FALSE(FileUtil::FileExists(ConfigFilePath()).ok());
719+
EXPECT_FALSE(StatsConfigUtil::IsEnabled());
720+
}
721+
722+
TEST_F(StatsConfigUtilTestMac, SetEnabledTrueRoundTrip) {
723+
EXPECT_TRUE(StatsConfigUtil::SetEnabled(true));
724+
EXPECT_TRUE(FileUtil::FileExists(ConfigFilePath()).ok());
725+
EXPECT_TRUE(StatsConfigUtil::IsEnabled());
726+
}
727+
728+
TEST_F(StatsConfigUtilTestMac, SetEnabledFalseRoundTrip) {
729+
EXPECT_TRUE(StatsConfigUtil::SetEnabled(false));
730+
EXPECT_TRUE(FileUtil::FileExists(ConfigFilePath()).ok());
731+
EXPECT_FALSE(StatsConfigUtil::IsEnabled());
732+
}
733+
734+
TEST_F(StatsConfigUtilTestMac, SetEnabledOverwritesPreviousValue) {
735+
EXPECT_TRUE(StatsConfigUtil::SetEnabled(true));
736+
EXPECT_TRUE(StatsConfigUtil::IsEnabled());
737+
EXPECT_TRUE(StatsConfigUtil::SetEnabled(false));
738+
EXPECT_FALSE(StatsConfigUtil::IsEnabled());
739+
EXPECT_TRUE(StatsConfigUtil::SetEnabled(true));
740+
EXPECT_TRUE(StatsConfigUtil::IsEnabled());
741+
}
742+
#endif // CHANNEL_DEV
743+
#endif // TARGET_OS_OSX
744+
677745
#ifdef __ANDROID__
678746
TEST(StatsConfigUtilTestAndroid, DefaultValueTest) {
679747
const TempFile config_file(testing::MakeTempFileOrDie());

0 commit comments

Comments
 (0)