Migrate old autosave XML files. (Disabled for now)
Code and tests to demonstrate migrating of older auto-save files, with blacklisting support to exclude properties. Disabled pending agreement on the required blacklisting values.
This commit is contained in:
parent
a9a85a618e
commit
3e1a701712
4 changed files with 287 additions and 1 deletions
|
@ -187,4 +187,5 @@ endif()
|
||||||
|
|
||||||
if (COMMAND flightgear_test)
|
if (COMMAND flightgear_test)
|
||||||
flightgear_test(posinit test_posinit.cxx)
|
flightgear_test(posinit test_posinit.cxx)
|
||||||
|
flightgear_test(autosaveMigration test_autosaveMigration.cxx)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -675,6 +675,103 @@ SGPath FGGlobals::autosaveFilePath(SGPath userDataPath) const
|
||||||
return simgear::Dir(userDataPath).file(autosaveName());
|
return simgear::Dir(userDataPath).file(autosaveName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void deleteProperties(SGPropertyNode* props, const string_list& blacklist)
|
||||||
|
{
|
||||||
|
const std::string path(props->getPath());
|
||||||
|
auto it = std::find_if(blacklist.begin(), blacklist.end(), [path](const std::string& black)
|
||||||
|
{ return simgear::strutils::matchPropPathToTemplate(path, black); });
|
||||||
|
if (it != blacklist.end()) {
|
||||||
|
SGPropertyNode* pr = props->getParent();
|
||||||
|
pr->removeChild(props);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse
|
||||||
|
for (int c=0; c < props->nChildren(); ++c) {
|
||||||
|
deleteProperties(props->getChild(c), blacklist);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
using VersionPair = std::pair<int, int>;
|
||||||
|
|
||||||
|
static bool operator<(const VersionPair& a, const VersionPair& b)
|
||||||
|
{
|
||||||
|
if (a.first == b.first) {
|
||||||
|
return a.second < b.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.first < b.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tryAutosaveMigration(const SGPath& userDataPath, SGPropertyNode* props)
|
||||||
|
{
|
||||||
|
const string_list versionParts = simgear::strutils::split(VERSION, ".");
|
||||||
|
if (versionParts.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
simgear::Dir userDataDir(userDataPath);
|
||||||
|
SGPath migratePath;
|
||||||
|
VersionPair foundVersion(0, 0);
|
||||||
|
const VersionPair currentVersion(simgear::strutils::to_int(versionParts[0]),
|
||||||
|
simgear::strutils::to_int(versionParts[1]));
|
||||||
|
|
||||||
|
for (auto previousSave : userDataDir.children(simgear::Dir::TYPE_FILE, ".xml")) {
|
||||||
|
const std::string base = previousSave.file_base();
|
||||||
|
VersionPair v;
|
||||||
|
// extract version from name
|
||||||
|
const int matches = ::sscanf(base.c_str(), "autosave_%d_%d", &v.first, &v.second);
|
||||||
|
if (matches != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVersion < v) {
|
||||||
|
// ignore autosaves from more recent versions; this happens when
|
||||||
|
// running unsable and stable at the same time
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v.first < 2000) {
|
||||||
|
// ignore autosaves from older versions, too much change to deal
|
||||||
|
// with.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundVersion < v) {
|
||||||
|
foundVersion = v;
|
||||||
|
migratePath = previousSave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!migratePath.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SG_LOG(SG_GENERAL, SG_INFO, "Migrating old autosave:" << migratePath);
|
||||||
|
SGPropertyNode oldProps;
|
||||||
|
|
||||||
|
try {
|
||||||
|
readProperties(migratePath, &oldProps, SGPropertyNode::USERARCHIVE);
|
||||||
|
} catch (sg_exception& e) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_WARN, "failed to read previous user settings:" << e.getMessage()
|
||||||
|
<< "(from " << e.getOrigin() << ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read migration blacklist
|
||||||
|
string_list blacklist;
|
||||||
|
for (auto node : fgGetNode("/sim/autosave-migration/blacklist")->getChildren("path")) {
|
||||||
|
blacklist.push_back(node->getStringValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply migration filters for each property in turn
|
||||||
|
deleteProperties(&oldProps, blacklist);
|
||||||
|
|
||||||
|
// copy remaining props out
|
||||||
|
copyProperties(&oldProps, props);
|
||||||
|
}
|
||||||
|
|
||||||
// Load user settings from the autosave file (normally in $FG_HOME)
|
// Load user settings from the autosave file (normally in $FG_HOME)
|
||||||
void
|
void
|
||||||
FGGlobals::loadUserSettings(SGPath userDataPath)
|
FGGlobals::loadUserSettings(SGPath userDataPath)
|
||||||
|
@ -697,6 +794,10 @@ FGGlobals::loadUserSettings(SGPath userDataPath)
|
||||||
SG_LOG(SG_INPUT, SG_WARN, "failed to read user settings:" << e.getMessage()
|
SG_LOG(SG_INPUT, SG_WARN, "failed to read user settings:" << e.getMessage()
|
||||||
<< "(from " << e.getOrigin() << ")");
|
<< "(from " << e.getOrigin() << ")");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
#if 0
|
||||||
|
tryAutosaveMigration(userDataPath, &autosave);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
copyProperties(&autosave, globals->get_props());
|
copyProperties(&autosave, globals->get_props());
|
||||||
}
|
}
|
||||||
|
|
166
src/Main/test_autosaveMigration.cxx
Normal file
166
src/Main/test_autosaveMigration.cxx
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
// Written by James Turner, started 2017.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 James Turner
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "unitTestHelpers.hxx"
|
||||||
|
|
||||||
|
#include <simgear/misc/test_macros.hxx>
|
||||||
|
#include <simgear/props/props_io.hxx>
|
||||||
|
#include <simgear/misc/sgstream.hxx>
|
||||||
|
#include <simgear/misc/sg_dir.hxx>
|
||||||
|
|
||||||
|
#include "Main/globals.hxx"
|
||||||
|
#include "Main/options.hxx"
|
||||||
|
#include "Main/fg_props.hxx"
|
||||||
|
|
||||||
|
using namespace flightgear;
|
||||||
|
|
||||||
|
void writeLegacyAutosave(SGPath userData, int majorVersion, int minorVersion)
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "autosave_" << majorVersion << "_" << minorVersion << ".xml";
|
||||||
|
|
||||||
|
sg_ofstream of(userData / os.str());
|
||||||
|
{
|
||||||
|
of << "<?xml version=\"1.0\"?>" \
|
||||||
|
"<PropertyList>" \
|
||||||
|
"<sim>" \
|
||||||
|
"<window-height>42</window-height>" \
|
||||||
|
"<presets>" \
|
||||||
|
"<foo>12</foo>" \
|
||||||
|
"<child><bar>12</bar></child>" \
|
||||||
|
"</presets>" \
|
||||||
|
"<presets n=\"1\">" \
|
||||||
|
"<foo>13</foo>" \
|
||||||
|
"</presets>" \
|
||||||
|
"<rendering>" \
|
||||||
|
"<msaa>10</msaa>" \
|
||||||
|
"<texture-size>512</texture-size>" \
|
||||||
|
"<texture-pack>" \
|
||||||
|
"<foo>abc</foo>" \
|
||||||
|
"<wibble>abc</wibble>" \
|
||||||
|
"</texture-pack>" \
|
||||||
|
"</rendering>" \
|
||||||
|
"<gui>" \
|
||||||
|
"<dialog n=\"1\">" \
|
||||||
|
"<widget>button</widget>" \
|
||||||
|
"</dialog>" \
|
||||||
|
"<dialog n=\"2\">" \
|
||||||
|
"<widget>slider</widget>" \
|
||||||
|
"</dialog>"\
|
||||||
|
"</gui>" \
|
||||||
|
"</sim>" \
|
||||||
|
"<some-setting>888</some-setting>" \
|
||||||
|
"<views>" \
|
||||||
|
"<view>" \
|
||||||
|
"<new-prop>somevalue</new-prop>" \
|
||||||
|
"<old-prop>somevalue</old-prop>" \
|
||||||
|
"</view>" \
|
||||||
|
"</views>" \
|
||||||
|
"</PropertyList>";
|
||||||
|
|
||||||
|
}
|
||||||
|
of.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeLegacyAutosave2(SGPath userData, int majorVersion, int minorVersion)
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "autosave_" << majorVersion << "_" << minorVersion << ".xml";
|
||||||
|
|
||||||
|
sg_ofstream of(userData / os.str());
|
||||||
|
{
|
||||||
|
of << "<?xml version=\"1.0\"?>" \
|
||||||
|
"<PropertyList>" \
|
||||||
|
"<sim>" \
|
||||||
|
"<bad>1</bad>" \
|
||||||
|
"</sim>" \
|
||||||
|
"</views>" \
|
||||||
|
"</PropertyList>";
|
||||||
|
|
||||||
|
}
|
||||||
|
of.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void testMigration()
|
||||||
|
{
|
||||||
|
fgtest::initTestGlobals("autosaveMigration");
|
||||||
|
|
||||||
|
Options::reset();
|
||||||
|
|
||||||
|
SGPath testUserDataPath = globals->get_fg_home() / "test_autosave_migrate";
|
||||||
|
if (!testUserDataPath.exists()) {
|
||||||
|
SGPath p = testUserDataPath / "foo";
|
||||||
|
p.create_dir(0755);
|
||||||
|
}
|
||||||
|
|
||||||
|
simgear::Dir homeDir(testUserDataPath);
|
||||||
|
for (auto path : homeDir.children(simgear::Dir::TYPE_FILE, ".xml")) {
|
||||||
|
path.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
writeLegacyAutosave(testUserDataPath, 2016, 1);
|
||||||
|
|
||||||
|
const string_list versionParts = simgear::strutils::split(VERSION, ".");
|
||||||
|
SG_VERIFY(versionParts.size() == 3);
|
||||||
|
const int currentMajor = simgear::strutils::to_int(versionParts[0]);
|
||||||
|
const int currentMinor = simgear::strutils::to_int(versionParts[1]);
|
||||||
|
|
||||||
|
// none of these should not be read
|
||||||
|
writeLegacyAutosave2(testUserDataPath, 2016, 0);
|
||||||
|
writeLegacyAutosave2(testUserDataPath, currentMajor, currentMinor + 1);
|
||||||
|
writeLegacyAutosave2(testUserDataPath, currentMajor+1, currentMinor + 1);
|
||||||
|
|
||||||
|
SGPath p = globals->autosaveFilePath(testUserDataPath);
|
||||||
|
if (p.exists()) {
|
||||||
|
SG_VERIFY(p.remove());
|
||||||
|
}
|
||||||
|
|
||||||
|
// write some blck-list rules to property tree
|
||||||
|
SGPropertyNode_ptr blacklist = fgGetNode("/sim/autosave-migration/blacklist", true);
|
||||||
|
blacklist->addChild("path")->setStringValue("/sim[0]/presets[0]/*");
|
||||||
|
blacklist->addChild("path")->setStringValue("/sim[0]/rendering[0]/texture-");
|
||||||
|
blacklist->addChild("path")->setStringValue("/views[0]/view[*]/old-prop");
|
||||||
|
blacklist->addChild("path")->setStringValue("/sim[0]/gui");
|
||||||
|
|
||||||
|
// execute method under test
|
||||||
|
globals->loadUserSettings(testUserDataPath);
|
||||||
|
|
||||||
|
SG_CHECK_EQUAL(globals->get_props()->getNode("sim")->getChildren("presets").size(), 2);
|
||||||
|
SG_CHECK_EQUAL(globals->get_props()->getNode("sim")->getChildren("gui").size(), 0);
|
||||||
|
|
||||||
|
SG_CHECK_EQUAL(globals->get_props()->getIntValue("sim/window-height"), 42);
|
||||||
|
SG_CHECK_EQUAL(globals->get_props()->getIntValue("sim/presets/foo"), 0);
|
||||||
|
SG_CHECK_EQUAL(globals->get_props()->getIntValue("sim/presets[1]/foo"), 13);
|
||||||
|
|
||||||
|
SG_CHECK_EQUAL(globals->get_props()->getIntValue("some-setting"), 888);
|
||||||
|
|
||||||
|
// if this is not zero, one of the bad autosaves was read
|
||||||
|
SG_CHECK_EQUAL(globals->get_props()->getIntValue("sim/bad"), 0);
|
||||||
|
|
||||||
|
|
||||||
|
fgtest::shutdownTestGlobals();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
testMigration();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -1,3 +1,21 @@
|
||||||
|
// Written by James Turner, started 2017.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 James Turner
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "unitTestHelpers.hxx"
|
#include "unitTestHelpers.hxx"
|
||||||
|
@ -114,6 +132,6 @@ int main(int argc, char* argv[])
|
||||||
testDefaultStartup();
|
testDefaultStartup();
|
||||||
testAirportOnlyStartup();
|
testAirportOnlyStartup();
|
||||||
testAirportAndMetarStartup();
|
testAirportAndMetarStartup();
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue