Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1076 enable backward compatibility #1740

Merged
merged 12 commits into from
Dec 4, 2023
2 changes: 2 additions & 0 deletions src/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_library(
main
comms.cpp
compatibility.cpp
cli.cpp
dissolve.cpp
io.cpp
Expand All @@ -14,6 +15,7 @@ add_library(
simulation.cpp
version.cpp
cli.h
compatibility.h
dissolve.h
keywords.h
version.h
Expand Down
41 changes: 41 additions & 0 deletions src/main/compatibility.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2023 Team Dissolve and contributors

#include "main/compatibility.h"
#include <iostream>

// Upgrade from version zero, which never supported TOML in the first
// place. This exists purely to allow testing the backward
// compatibility code before there is any pass worth checking.
SerialisedValue unreal_upgrade(SerialisedValue contents)
rprospero marked this conversation as resolved.
Show resolved Hide resolved
{
if (contents["pairPotentials"].contains("coulombTruncation"))
contents["pairPotentials"]["coulombTruncation"] =
toml::find<std::string>(contents["pairPotentials"], "coulombTruncation") + "ed";

return contents;
}

// Upgrade a file to the current version.
SerialisedValue backwards_upgrade(SerialisedValue contents)
rprospero marked this conversation as resolved.
Show resolved Hide resolved
{
using Version::DissolveVersion;

// A map of version numbers and the corresponding function to
// upgrade the file.
std::map<std::string, std::function<SerialisedValue(SerialisedValue)>> breaking_changes = {{"1.2.0", unreal_upgrade}};
rprospero marked this conversation as resolved.
Show resolved Hide resolved

DissolveVersion current(toml::find<std::string>(contents, "version"));

for (auto &[key, value] : breaking_changes)
{
DissolveVersion next(key);
if (current < next)
{
contents = value(contents);
current = next;
}
}

return contents;
}
9 changes: 9 additions & 0 deletions src/main/compatibility.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2023 Team Dissolve and contributors

#pragma once

#include "base/serialiser.h"
#include "main/version.h"

rprospero marked this conversation as resolved.
Show resolved Hide resolved
SerialisedValue backwards_upgrade(SerialisedValue contents);
14 changes: 13 additions & 1 deletion src/main/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@
// Copyright (c) 2023 Team Dissolve and contributors

#include "base/lineParser.h"
#include "base/messenger.h"
#include "base/serialiser.h"
#include "base/sysFunc.h"
#include "classes/atomType.h"
#include "classes/species.h"
#include "data/isotopes.h"
#include "main/compatibility.h"
#include "main/dissolve.h"
#include "main/keywords.h"
#include "main/version.h"
#include <cstring>
#include <functional>
#include <map>

// Load input file through supplied parser
bool Dissolve::loadInput(LineParser &parser)
Expand Down Expand Up @@ -144,6 +149,8 @@ SerialisedValue Dissolve::serialise() const
{
SerialisedValue root;

root["version"] = Version::semantic();

if (!coreData_.masterBonds().empty() || !coreData_.masterAngles().empty() || !coreData_.masterTorsions().empty() ||
!coreData_.masterImpropers().empty())
root["master"] = coreData_.serialiseMaster();
Expand Down Expand Up @@ -183,8 +190,13 @@ void Dissolve::deserialisePairPotentials(const SerialisedValue &node)
}

// Read values from a serialisable value
void Dissolve::deserialise(const SerialisedValue &node)
void Dissolve::deserialise(const SerialisedValue &node_orig)
rprospero marked this conversation as resolved.
Show resolved Hide resolved
{
// Default to current version if no version info is given.
bool hasVersion = node_orig.contains("version");
rprospero marked this conversation as resolved.
Show resolved Hide resolved
if (!hasVersion)
Messenger::warn("File does not contain version information. Assuming the current version: {}", Version::semantic());
const SerialisedValue node = hasVersion ? backwards_upgrade(node_orig) : node_orig;

Serialisable::optionalOn(node, "pairPotentials", [this](const auto node) { deserialisePairPotentials(node); });
Serialisable::optionalOn(node, "master", [this](const auto node) { coreData_.deserialiseMaster(node); });
Expand Down
29 changes: 29 additions & 0 deletions src/main/version.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,33 @@ std::string_view appType()
#endif
}

DissolveVersion::DissolveVersion(std::string_view version)
{
auto dot = version.find(".");
major_ = std::stoi(std::string(version.substr(0, dot)));
auto rest = version.substr(dot + 1);
dot = rest.find(".");
minor_ = std::stoi(std::string(rest.substr(0, dot)));
patch_ = std::stoi(std::string(rest.substr(dot + 1)));
}

bool DissolveVersion::operator<(const DissolveVersion &other) const
{
if (major_ != other.major_)
return major_ < other.major_;
if (minor_ != other.minor_)
return minor_ < other.minor_;
return patch_ < other.patch_;
}

bool DissolveVersion::operator==(const DissolveVersion &other) const
{
return major_ == other.major_ && minor_ == other.minor_ && patch_ == other.patch_;
}

std::ostream &operator<<(std::ostream &os, const DissolveVersion &version)
{
os << fmt::format("{}.{}.{}", version.major_, version.minor_, version.patch_);
return os;
}
}; // namespace Version
17 changes: 17 additions & 0 deletions src/main/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,21 @@ std::string_view info();
std::string_view repoUrl();
// Return app type
std::string_view appType();

// This class handles the version numbers of the app in a structured
// manor.
rprospero marked this conversation as resolved.
Show resolved Hide resolved
class DissolveVersion
{
private:
int major_;
int minor_;
int patch_;

public:
DissolveVersion(std::string_view version);
bool operator<(const DissolveVersion &other) const;
bool operator==(const DissolveVersion &other) const;
friend std::ostream &operator<<(std::ostream &os, const DissolveVersion &version);
};

}; // namespace Version
1 change: 1 addition & 0 deletions tests/io/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
dissolve_add_test(SRC cif.cpp)
dissolve_add_test(SRC intraParameterParse.cpp)
dissolve_add_test(SRC version.cpp)
57 changes: 57 additions & 0 deletions tests/io/version.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2023 Team Dissolve and contributors

#include "main/version.h"
#include "base/serialiser.h"
#include "main/dissolve.h"
#include <gtest/gtest.h>

namespace UnitTest
{

TEST(VersionTest, VersionClass)
{
using Version::DissolveVersion;
DissolveVersion first{"1.10.3"}, second{"2.1.12"}, minor{"2.2.1"}, patch{"2.2.2"}, third{"3.0.0"};

// Main sequence
EXPECT_LT(first, second);
EXPECT_LT(second, minor);
EXPECT_LT(minor, patch);
EXPECT_LT(patch, third);

// Full combinatorial
EXPECT_LT(first, minor);
EXPECT_LT(first, patch);
EXPECT_LT(first, third);
EXPECT_LT(second, minor);
EXPECT_LT(second, patch);
EXPECT_LT(second, third);
EXPECT_LT(minor, third);
}

TEST(VersionTest, VersionInfo)
{
CoreData coreData;
Dissolve dissolve(coreData);
dissolve.loadInput("dissolve/input/rdfMethod.txt");
auto serialised = dissolve.serialise();

Version::DissolveVersion fileVersion(serialised["version"].as_string().str), actual(Version::semantic());

EXPECT_EQ(fileVersion, actual);
}

TEST(VersionTest, VersionUpgrade)
{
CoreData coreData;
Dissolve dissolve(coreData);

SerialisedValue old;
old["version"] = "0.0.0";
old["pairPotentials"] = {{"coulombTruncation", "Shift"}};

dissolve.deserialise(old);
}

} // namespace UnitTest
Loading