Centralize settings write notification logic

This commit is contained in:
Carlos Zamora
2026-06-05 16:21:23 -07:00
parent 07d351365d
commit 7d6b7d3f1f
16 changed files with 224 additions and 93 deletions

View File

@@ -95,6 +95,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// there are some other cases as well
Model::ActionAndArgs newActionAndArgs{ actionEnum, emptyArgs };
_command.ActionAndArgs(newActionAndArgs);
get_self<ActionsViewModel>(actionsPageVM)->NotifyUserActionEdited();
if (_IsNewCommand)
{
actionsPageVM.RegenerateCommandID(_command);
@@ -129,6 +130,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (_command.Name() != newName)
{
_command.Name(newName);
if (const auto actionsPageVM{ _actionsPageVM.get() })
{
get_self<ActionsViewModel>(actionsPageVM)->NotifyUserActionEdited();
}
_NotifyChanges(L"DisplayName", L"DisplayNameAndKeyChordAutomationPropName");
_cachedDisplayName.clear();
}
@@ -309,6 +314,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
actionArgsVM.WrapperValueChanged([weakThis = get_weak()](const IInspectable& /*sender*/, const IInspectable& /*args*/) {
if (auto weak = weakThis.get())
{
if (const auto actionsPageVM{ weak->_actionsPageVM.get() })
{
get_self<ActionsViewModel>(actionsPageVM)->NotifyUserActionEdited();
}
// for new commands, make sure we generate a new ID every time any arg value changes
if (weak->_IsNewCommand)
{
@@ -1376,6 +1385,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_Settings.UpdateCommandID(command, {});
}
void ActionsViewModel::NotifyUserActionEdited()
{
if (_Settings)
{
if (const auto actionMap{ _Settings.ActionMap() })
{
actionMap.NotifyWriteSettings();
}
}
}
void ActionsViewModel::_CmdVMEditRequestedHandler(const Editor::CommandViewModel& senderVM, const IInspectable& /*args*/)
{
CurrentCommand(senderVM);

View File

@@ -268,6 +268,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void AttemptAddOrModifyKeyChord(const Editor::KeyChordViewModel& senderVM, winrt::hstring commandID, const Control::KeyChord& newKeys, const Control::KeyChord& oldKeys);
void AddCopiedCommand(const Model::Command& newCommand);
void RegenerateCommandID(const Model::Command& command);
void NotifyUserActionEdited();
Windows::Foundation::Collections::IMap<Model::ShortcutAction, winrt::hstring> AvailableShortcutActionsAndNames();
Windows::Foundation::Collections::IMap<winrt::hstring, Model::ShortcutAction> NameToActionMap();

View File

@@ -713,7 +713,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
if (!name.empty())
{
_NestedCommands.emplace(name, cmd);
_NotifyWriteSettings();
NotifyWriteSettings();
}
return;
}
@@ -722,7 +722,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
if (cmdImpl->IterateOn() != ExpandCommandType::None)
{
_IterableCommands.emplace_back(cmd);
_NotifyWriteSettings();
NotifyWriteSettings();
return;
}
@@ -732,7 +732,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_TryUpdateActionMap(cmd);
_TryUpdateKeyChord(cmd, keys);
_NotifyWriteSettings();
NotifyWriteSettings();
}
// Method Description:
@@ -974,7 +974,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_KeyMap.insert_or_assign(oldKeys, L"");
}
_NotifyWriteSettings();
NotifyWriteSettings();
return true;
}
@@ -999,7 +999,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// set to unbound in this layer
_KeyMap.emplace(keys, L"");
}
_NotifyWriteSettings();
NotifyWriteSettings();
}
void ActionMap::AddKeyBinding(Control::KeyChord keys, const winrt::hstring& cmdID)
@@ -1007,7 +1007,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_KeyMap.insert_or_assign(keys, cmdID);
_changeLog.emplace(KeysKey);
_RefreshKeyBindingCaches();
_NotifyWriteSettings();
NotifyWriteSettings();
}
// Method Description:
@@ -1030,7 +1030,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
_ActionMap.erase(cmdID);
_RefreshKeyBindingCaches();
_NotifyWriteSettings();
NotifyWriteSettings();
}
// This is a helper to aid in sorting commands by their `Name`s, alphabetically.
@@ -1250,7 +1250,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_KeyMap.erase(keys);
_KeyMap.emplace(keys, newID);
}
_NotifyWriteSettings();
NotifyWriteSettings();
}
_RefreshKeyBindingCaches();
}

View File

@@ -45,5 +45,6 @@ namespace Microsoft.Terminal.Settings.Model
void AddKeyBinding(Microsoft.Terminal.Control.KeyChord keys, String cmdID);
void RegisterKeyBinding(Microsoft.Terminal.Control.KeyChord keys, ActionAndArgs action);
void AddSendInputAction(String name, String input, Microsoft.Terminal.Control.KeyChord keys);
void NotifyWriteSettings();
}
}

View File

@@ -1540,6 +1540,14 @@ void CascadiaSettings::_installWriteSink()
{
winrt::get_self<implementation::ActionMap>(am)->SetWriteSettingsSink(_writeSink);
}
for (const auto& entry : _globals->ColorSchemes())
{
winrt::get_self<implementation::ColorScheme>(entry.Value())->SetWriteSettingsSink(_writeSink);
}
for (const auto& entry : _globals->Themes())
{
winrt::get_self<implementation::Theme>(entry.Value())->SetWriteSettingsSink(_writeSink);
}
}
installForProfile(_baseLayerProfile.get());

View File

@@ -209,6 +209,7 @@ winrt::hstring ColorScheme::Name() const
void ColorScheme::Name(const winrt::hstring& value)
{
JsonUtils::SetValueForKeyPreservingComments(_json, NameKey, value);
NotifyWriteSettings();
}
// Reads a color from _json, falling back to defaultValue when the key is absent.
@@ -223,6 +224,7 @@ winrt::Microsoft::Terminal::Core::Color ColorScheme::_getColor(std::string_view
void ColorScheme::_setColor(std::string_view key, const Core::Color& value)
{
JsonUtils::SetValueForKeyPreservingComments(_json, key, value);
NotifyWriteSettings();
}
#define GEN_NAMED_COLOR_ACCESSOR(name, jsonKey, defaultVal) \

View File

@@ -19,12 +19,13 @@ Author(s):
#include "../../inc/conattrs.hpp"
#include "DefaultSettings.h"
#include "JsonUtils.h"
#include "SettingsWriteNotifier.h"
#include "ColorScheme.g.h"
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct ColorScheme : ColorSchemeT<ColorScheme>
struct ColorScheme : ColorSchemeT<ColorScheme>, WriteNotifiable
{
// A ColorScheme constructed with uninitialized_t
// leaves _json empty to be populated lazily.

View File

@@ -277,16 +277,14 @@ void GlobalAppSettings::LayerActionsFrom(const Json::Value& json, const OriginTa
void GlobalAppSettings::AddColorScheme(const Model::ColorScheme& scheme)
{
_colorSchemes.Insert(scheme.Name(), scheme);
// Coarse-grained auto-save coverage (GH#12424): color schemes aren't
// IInheritable, so add/remove is notified here at the collection level. (No-op
// until this tree is the live, bound tree.)
_NotifyWriteSettings();
winrt::get_self<ColorScheme>(scheme)->SetWriteSettingsSink(_writeSettingsSink);
NotifyWriteSettings();
}
void GlobalAppSettings::RemoveColorScheme(hstring schemeName)
{
_colorSchemes.TryRemove(schemeName);
_NotifyWriteSettings();
NotifyWriteSettings();
}
winrt::Microsoft::Terminal::Settings::Model::ColorScheme GlobalAppSettings::DuplicateColorScheme(const Model::ColorScheme& source)
@@ -460,9 +458,8 @@ winrt::Microsoft::Terminal::Settings::Model::Theme GlobalAppSettings::CurrentThe
void GlobalAppSettings::AddTheme(const Model::Theme& theme)
{
_themes.Insert(theme.Name(), theme);
// Coarse-grained auto-save coverage (GH#12424): themes aren't IInheritable,
// so add is notified here at the collection level. (No-op until bound.)
_NotifyWriteSettings();
winrt::get_self<implementation::Theme>(theme)->SetWriteSettingsSink(_writeSettingsSink);
NotifyWriteSettings();
}
winrt::Windows::Foundation::Collections::IMapView<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::Theme> GlobalAppSettings::Themes() noexcept

View File

@@ -19,38 +19,12 @@ Author(s):
#include "JsonUtils.h"
#include "JsonSyncCollections.h"
#include "SettingsWriteNotifier.h"
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
// A late-bound, shared "request auto-save" notifier (GH#12424). One instance
// is created by CascadiaSettings and installed (by shared_ptr) on every
// object in the *live* settings tree. When a setting is mutated, the object
// calls Notify(); CascadiaSettings binds the actual handler after a
// successful load via SetHandler(). Until a handler is bound, Notify() is a
// no-op. The notifier is deliberately NOT propagated by Copy()/clone paths,
// so editor clones (and the defaults fallback) never auto-save the user's
// settings.json.
struct SettingsWriteNotifier
{
void Notify() const
{
if (_handler)
{
_handler();
}
}
void SetHandler(std::function<void()> handler)
{
_handler = std::move(handler);
}
private:
std::function<void()> _handler;
};
template<typename T>
struct IInheritable
struct IInheritable : WriteNotifiable
{
public:
// Method Description:
@@ -94,33 +68,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return _parents;
}
// A shared "request auto-save" sink. The owning CascadiaSettings
// creates one notifier and installs it on every object in the *live*
// tree. When a setting on this layer is mutated, _NotifyWriteSettings()
// invokes the notifier, which requests a debounced auto-save. The notifier
// is intentionally NOT copied by Copy()/clone paths, so editor clones (and
// the defaults fallback) never auto-save the user's settings.json.
using WriteSettingsSink = std::shared_ptr<SettingsWriteNotifier>;
void SetWriteSettingsSink(const WriteSettingsSink& sink)
{
_writeSettingsSink = sink;
}
// The shared "request auto-save" sink (SetWriteSettingsSink,
// _writeSettingsSink, NotifyWriteSettings) is inherited from
// WriteNotifiable. The owning CascadiaSettings installs one shared sink on
// every object in the *live* tree; mutators call NotifyWriteSettings().
// The sink is intentionally NOT copied by Copy()/clone paths, so editor
// clones (and the defaults fallback) never auto-save the user's settings.
protected:
std::vector<com_ptr<T>> _parents{};
WriteSettingsSink _writeSettingsSink{};
// Invoked by setters/clears to request an auto-save of the owning tree.
// No-op until a sink is installed (i.e. only on the committed live tree).
void _NotifyWriteSettings() const
{
if (_writeSettingsSink)
{
_writeSettingsSink->Notify();
}
}
// Method Description:
// - Actions to be performed after a child was created. Generally used to set
// any extraneous data from the parent into the child.
@@ -204,7 +161,7 @@ public:
void Clear##name() \
{ \
_json.removeMember(JsonKey(jsonKey)); \
_NotifyWriteSettings(); \
NotifyWriteSettings(); \
} \
\
private: \
@@ -273,7 +230,7 @@ public: \
} \
::Microsoft::Terminal::Settings::Model::JsonUtils::SetValueForKey( \
_json, jsonKey, value); \
_NotifyWriteSettings(); \
NotifyWriteSettings(); \
}
// JSON-backed inheritable setting.
@@ -295,7 +252,7 @@ public: \
{ \
::Microsoft::Terminal::Settings::Model::JsonUtils::SetValueForKey( \
_json, jsonKey, value); \
_NotifyWriteSettings(); \
NotifyWriteSettings(); \
}
// JSON-backed inheritable setting with change logging.
@@ -340,7 +297,7 @@ public:
/* explicitly set to null (not the same as clearing) */ \
_json[JsonKey(jsonKey)] = Json::nullValue; \
} \
_NotifyWriteSettings(); \
NotifyWriteSettings(); \
}
// =============================================================================
@@ -466,7 +423,7 @@ public:
_##name = value; \
::Microsoft::Terminal::Settings::Model::JsonUtils::SetValueForKey(_json, jsonKey, value); \
_logSettingSet(jsonKey); \
_NotifyWriteSettings(); \
NotifyWriteSettings(); \
} \
\
/* Dual-clear: backing field + _json key. Both must clear so auto-save doesn't */ \
@@ -476,7 +433,7 @@ public:
_##name = std::nullopt; \
_json.removeMember(JsonKey(jsonKey)); \
_logSettingSet(jsonKey); \
_NotifyWriteSettings(); \
NotifyWriteSettings(); \
}
// IMediaResource vector setting (BellSound): needs both IMediaResource
@@ -516,7 +473,7 @@ public:
.ToJson(current); \
strong->_json[JsonKey(jsonKey)] = std::move(temp); \
strong->_logSettingSet(jsonKey); \
strong->_NotifyWriteSettings(); \
strong->NotifyWriteSettings(); \
}); \
} \
return *val; \
@@ -528,7 +485,7 @@ public:
_##name = value; \
::Microsoft::Terminal::Settings::Model::JsonUtils::SetValueForKey(_json, jsonKey, value); \
_logSettingSet(jsonKey); \
_NotifyWriteSettings(); \
NotifyWriteSettings(); \
} \
\
/* Dual-clear: backing field + _json key. */ \
@@ -537,7 +494,7 @@ public:
_##name = std::nullopt; \
_json.removeMember(JsonKey(jsonKey)); \
_logSettingSet(jsonKey); \
_NotifyWriteSettings(); \
NotifyWriteSettings(); \
}
// JSON-backed collection settings (IVector<T> / IMap<K,V>) that callers mutate
@@ -568,7 +525,7 @@ public:
auto temp = ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<type>{}.ToJson(current); \
strong->_json[JsonKey(jsonKey)] = std::move(temp); \
strong->_logSettingSet(jsonKey); \
strong->_NotifyWriteSettings(); \
strong->NotifyWriteSettings(); \
}); \
} \
\
@@ -595,7 +552,7 @@ public:
auto temp = ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<type>{}.ToJson(current); \
strong->_json[JsonKey(jsonKey)] = std::move(temp); \
strong->_logSettingSet(jsonKey); \
strong->_NotifyWriteSettings(); \
strong->NotifyWriteSettings(); \
}); \
} \
\

View File

@@ -90,6 +90,7 @@
<DependentUpon>JsonManager.idl</DependentUpon>
</ClInclude>
<ClInclude Include="JsonSyncCollections.h" />
<ClInclude Include="SettingsWriteNotifier.h" />
<ClInclude Include="MTSMSettings.h" />
<ClInclude Include="IDynamicProfileGenerator.h" />
<ClInclude Include="JsonUtils.h" />

View File

@@ -82,6 +82,7 @@
<ClInclude Include="JsonSyncCollections.h">
<Filter>json</Filter>
</ClInclude>
<ClInclude Include="SettingsWriteNotifier.h" />
<ClInclude Include="IInheritable.h" />
<ClInclude Include="JsonManager.h" />
<ClInclude Include="MTSMSettings.h" />

View File

@@ -121,7 +121,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
_entries = std::nullopt;
_json = Json::Value{ Json::arrayValue };
_NotifyWriteSettings();
NotifyWriteSettings();
}
void NewTabMenu::ReplaceAll(const IVector<Model::NewTabMenuEntry>& entries)
@@ -377,7 +377,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
_json = std::move(arr);
_emitChangeLog();
_NotifyWriteSettings();
NotifyWriteSettings();
}
void NewTabMenu::_emitChangeLog()

View File

@@ -0,0 +1,62 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- SettingsWriteNotifier.h
Abstract:
- The late-bound "request auto-save" sink shared by every settings-model object
in the live tree.
Author(s):
- Carlos Zamora - June 2026
--*/
#pragma once
#include <memory>
#include <functional>
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct SettingsWriteNotifier
{
void Notify() const
{
if (_handler)
{
_handler();
}
}
void SetHandler(std::function<void()> handler)
{
_handler = std::move(handler);
}
private:
std::function<void()> _handler;
};
struct WriteNotifiable
{
public:
using WriteSettingsSink = std::shared_ptr<SettingsWriteNotifier>;
void SetWriteSettingsSink(const WriteSettingsSink& sink)
{
_writeSettingsSink = sink;
}
void NotifyWriteSettings() const
{
if (_writeSettingsSink)
{
_writeSettingsSink->Notify();
}
}
protected:
WriteSettingsSink _writeSettingsSink{};
};
}

View File

@@ -248,6 +248,26 @@ winrt::com_ptr<Theme> Theme::Copy() const
return theme;
}
void Theme::SetWriteSettingsSink(const WriteNotifiable::WriteSettingsSink& sink)
{
if (_Window)
{
winrt::get_self<implementation::WindowTheme>(_Window)->SetWriteSettingsSink(sink);
}
if (_Settings)
{
winrt::get_self<implementation::SettingsTheme>(_Settings)->SetWriteSettingsSink(sink);
}
if (_TabRow)
{
winrt::get_self<implementation::TabRowTheme>(_TabRow)->SetWriteSettingsSink(sink);
}
if (_Tab)
{
winrt::get_self<implementation::TabTheme>(_Tab)->SetWriteSettingsSink(sink);
}
}
// Method Description:
// - Create a new instance of this class from a serialized JsonObject.
// Arguments:

View File

@@ -27,6 +27,7 @@ Author(s):
#include "Theme.g.h"
#include "JsonUtils.h"
#include "SettingsWriteNotifier.h"
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
@@ -85,6 +86,7 @@ public:
void name(const type& value) \
{ \
::Microsoft::Terminal::Settings::Model::JsonUtils::SetValueForKey(_json, jsonKey, value); \
NotifyWriteSettings(); \
} \
bool Has##name() const \
{ \
@@ -93,10 +95,11 @@ public:
void Clear##name() \
{ \
_json.removeMember(jsonKey); \
NotifyWriteSettings(); \
}
#define THEME_OBJECT(className, macro) \
struct className : className##T<className> \
struct className : className##T<className>, WriteNotifiable \
{ \
winrt::com_ptr<className> Copy(); \
Json::Value ToJson(); \
@@ -126,6 +129,7 @@ public:
static com_ptr<Theme> FromJson(const Json::Value& json);
Json::Value ToJson() const;
void LogSettingChanges(std::set<std::string>& changes, const std::string_view& context);
void SetWriteSettingsSink(const WriteNotifiable::WriteSettingsSink& sink);
winrt::Windows::UI::Xaml::ElementTheme RequestedTheme() const noexcept;

View File

@@ -5,6 +5,7 @@
#include "../TerminalSettingsModel/CascadiaSettings.h"
#include "../TerminalSettingsModel/ColorScheme.h"
#include "../TerminalSettingsModel/Theme.h"
#include "../TerminalSettingsModel/ActionMap.h"
#include "../TerminalSettingsModel/FileUtils.h"
#include "JsonTestClass.h"
@@ -30,6 +31,8 @@ namespace SettingsModelUnitTests
TEST_METHOD(WriteHandlerFiresOnSetterAndClear);
TEST_METHOD(ClonedSettingsDoNotFireWriteHandler);
TEST_METHOD(CollectionEditsFireWriteHandler);
TEST_METHOD(ColorSchemePerPropertyEditsFireWriteHandler);
TEST_METHOD(ThemeSubObjectEditsFireWriteHandler);
TEST_METHOD(ActionMapEditsFireWriteHandler);
private:
@@ -164,9 +167,6 @@ namespace SettingsModelUnitTests
void JsonManagerTests::CollectionEditsFireWriteHandler()
{
// Color schemes / themes aren't IInheritable, so per-property edits aren't
// covered, but add/remove is wired at the GlobalAppSettings collection
// level. Verify that coarse-grained coverage requests a save.
static constexpr std::string_view settingsJson{ R"({
"profiles": {
"list": [
@@ -191,11 +191,67 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(2, writeRequests);
}
void JsonManagerTests::ColorSchemePerPropertyEditsFireWriteHandler()
{
static constexpr std::string_view settingsJson{ R"({
"profiles": {
"list": [
{
"name": "profile0",
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
}
]
}
})" };
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settingsJson);
auto writeRequests = 0;
settings->SetWriteHandler([&]() { ++writeRequests; });
// Adding the scheme forwards the sink to it; the add itself requests a save.
const auto scheme = winrt::make<implementation::ColorScheme>(winrt::hstring{ L"My Scheme" });
settings->GlobalSettings().AddColorScheme(scheme);
VERIFY_ARE_EQUAL(1, writeRequests);
// Each per-property edit requests a save.
scheme.Foreground(til::color{ 0x10, 0x20, 0x30 });
VERIFY_ARE_EQUAL(2, writeRequests);
scheme.SetColorTableEntry(0, til::color{ 0x40, 0x50, 0x60 });
VERIFY_ARE_EQUAL(3, writeRequests);
scheme.Name(L"Renamed Scheme");
VERIFY_ARE_EQUAL(4, writeRequests);
}
void JsonManagerTests::ThemeSubObjectEditsFireWriteHandler()
{
static constexpr std::string_view themeJson{ R"({
"name": "MyTheme",
"window": { "useMica": false }
})" };
const auto theme = implementation::Theme::FromJson(VerifyParseSucceeded(themeJson));
VERIFY_IS_NOT_NULL(theme);
auto writeRequests = 0;
const auto sink = std::make_shared<implementation::SettingsWriteNotifier>();
sink->SetHandler([&]() { ++writeRequests; });
theme->SetWriteSettingsSink(sink);
const auto window = theme->Window();
VERIFY_IS_NOT_NULL(window);
winrt::get_self<implementation::WindowTheme>(window)->UseMica(true);
VERIFY_ARE_EQUAL(1, writeRequests);
winrt::get_self<implementation::WindowTheme>(window)->ClearUseMica();
VERIFY_ARE_EQUAL(2, writeRequests);
}
void JsonManagerTests::ActionMapEditsFireWriteHandler()
{
// ActionMap isn't JSON-backed, but its mutators request a save directly and
// it's registered with the write sink, so user action/keybinding edits must
// request an auto-save.
static constexpr std::string_view settingsJson{ R"({
"profiles": {
"list": [
@@ -246,10 +302,10 @@ namespace SettingsModelUnitTests
actionMap.DeleteUserCommand(L"Test.NewTab");
VERIFY_ARE_EQUAL(5, writeRequests);
// The editor works on a deep clone, which must never auto-save the live
// file: a clone's ActionMap edit must NOT invoke the handler.
const auto clone = settings->Copy();
clone.GlobalSettings().ActionMap().AddKeyBinding(ctrlT, L"Test.NewTab");
VERIFY_ARE_EQUAL(5, writeRequests);
// An in-place editor edit (rename/action/arg change mutates the Command
// directly, bypassing the mutators above) is signaled via the projected
// NotifyWriteSettings and must request a save.
actionMap.NotifyWriteSettings();
VERIFY_ARE_EQUAL(6, writeRequests);
}
}