KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_settings_manager.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
24
26
30#include <settings/parameters.h>
32
33#include <json_common.h>
34
35#include <filesystem>
36#include <fstream>
37#include <sstream>
38#include <system_error>
39
40namespace fs = std::filesystem;
41
42
43// Backed by a caller-controlled absolute path (SETTINGS_LOC::NONE) so the tests persist and
44// reload from a scratch directory without touching the shared config corpus.
46{
47public:
48 FLUSH_TEST_SETTINGS( const wxString& aFullPath ) :
49 JSON_SETTINGS( aFullPath, SETTINGS_LOC::NONE, 1 ),
50 m_value( 0 )
51 {
52 m_params.emplace_back( new PARAM<int>( "test.value", &m_value, 0 ) );
53 }
54
56};
57
58
60{
62 m_tempDir( fs::temp_directory_path() / "kicad_settings_manager_test" )
63 {
64 std::error_code ec;
65 fs::remove_all( m_tempDir, ec );
66
67 // Throwing overload so an unusable scratch directory fails setup loudly
68 fs::create_directories( m_tempDir );
69 }
70
72 {
73 std::error_code ec;
74 fs::remove_all( m_tempDir, ec );
75 }
76
77 wxString Path( const std::string& aName ) const
78 {
79 return wxString( ( m_tempDir / aName ).string() );
80 }
81
82 fs::path m_tempDir;
83};
84
85
86BOOST_FIXTURE_TEST_SUITE( SettingsManager, SETTINGS_MANAGER_FIXTURE )
87
88
89// Load() may run after a settings object was edited in memory but not yet written; the pending
90// edit must be flushed before reloading or the stale on-disk copy silently discards it.
91BOOST_AUTO_TEST_CASE( LoadFlushesDirtySettings )
92{
94
95 // Drop the auto-registered common settings so Load() only touches the scratch object
96 mgr.FlushAndRelease( mgr.GetCommonSettings(), false );
97
99 mgr.RegisterSettings( new FLUSH_TEST_SETTINGS( Path( "dirty" ) ), false );
100
101 cfg->SaveToFile();
102 cfg->m_value = 42;
103
104 mgr.Load();
105
106 BOOST_CHECK_EQUAL( cfg->m_value, 42 );
107
108 // The dirty value must have reached disk, not merely survived in memory
109 FLUSH_TEST_SETTINGS fresh( Path( "dirty" ) );
110 fresh.LoadFromFile();
111 BOOST_CHECK_EQUAL( fresh.m_value, 42 );
112}
113
114
115// A registered object that has never been synchronized with its file must not be flushed by
116// Load(); flushing would overwrite the file with construction state before it is ever read.
117BOOST_AUTO_TEST_CASE( LoadDoesNotFlushNeverSyncedSettings )
118{
119 {
120 FLUSH_TEST_SETTINGS seed( Path( "cold" ) );
121 seed.m_value = 7;
122 seed.SaveToFile();
123 }
124
126 mgr.FlushAndRelease( mgr.GetCommonSettings(), false );
127
129 mgr.RegisterSettings( new FLUSH_TEST_SETTINGS( Path( "cold" ) ), false );
130
131 mgr.Load();
132
133 BOOST_CHECK_EQUAL( cfg->m_value, 7 );
134
135 FLUSH_TEST_SETTINGS fresh( Path( "cold" ) );
136 fresh.LoadFromFile();
137 BOOST_CHECK_EQUAL( fresh.m_value, 7 );
138}
139
140
141// An incomplete color theme (missing keys added by a newer build) must not be rewritten merely
142// to inject default colors when the user made no change, mirroring the .kicad_pro guarantee.
143//
144// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/24402
145BOOST_AUTO_TEST_CASE( ColorThemeNotRewrittenWhenUnchanged )
146{
147 // Canonical theme written with KiCad's own writer so the reload round-trip is clean.
148 {
149 COLOR_SETTINGS seed( Path( "theme" ), true );
150 seed.SaveToFile( wxEmptyString, true );
151 }
152
153 fs::path themePath = m_tempDir / "theme.json";
154
155 auto readFile = []( const fs::path& aPath )
156 {
157 std::ifstream in( aPath );
158 std::stringstream buffer;
159 buffer << in.rdbuf();
160 return buffer.str();
161 };
162
163 // Drop a whole colored section so the file mimics a theme saved before those colors existed.
164 // Their in-memory values load as defaults, so a no-op load must not resurrect them.
165 {
166 nlohmann::json js = nlohmann::json::parse( readFile( themePath ) );
167 BOOST_REQUIRE( js.contains( "gerbview" ) );
168 js.erase( "gerbview" );
169
170 std::ofstream out( themePath );
171 out << std::setw( 2 ) << js << std::endl;
172 out.close();
173 }
174
175 std::string before = readFile( themePath );
176
177 COLOR_SETTINGS cfg( Path( "theme" ), true );
178 cfg.LoadFromFile();
179
180 BOOST_CHECK( !cfg.SaveToFile( wxEmptyString ) );
181 BOOST_CHECK_EQUAL( before, readFile( themePath ) );
182}
183
184
Color settings are a bit different than most of the settings objects in that there can be more than o...
FLUSH_TEST_SETTINGS(const wxString &aFullPath)
virtual bool LoadFromFile(const wxString &aDirectory="")
Loads the backing file from disk and then calls Load()
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
JSON_SETTINGS(const wxString &aFilename, SETTINGS_LOC aLocation, int aSchemaVersion)
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
Calls Store() and then writes the contents of the JSON document to a file.
T * RegisterSettings(T *aSettings, bool aLoadNow=true)
Take ownership of the pointer passed in.
COMMON_SETTINGS * GetCommonSettings() const
Retrieve the common settings shared by all applications.
void FlushAndRelease(JSON_SETTINGS *aSettings, bool aSave=true)
If the given settings object is registered, save it to disk and unregister it.
@ NONE
Definition eda_shape.h:72
SETTINGS_LOC
static bool readFile(const wxString &aFileName, wxString &aOut, size_t aLimit=0)
Read a file into aOut.
wxString Path(const std::string &aName) const
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(LoadFlushesDirtySettings)
BOOST_CHECK_EQUAL(result, "25.4")