Merge pull request #21995 from chennes/moveBackupPolicy
Extract BackupPolicy class into its own file to facilitate automated testing
This commit is contained in:
@@ -47,13 +47,27 @@ runs:
|
||||
testCommand: ${{ inputs.builddir }}/tests/Assembly_tests_run --gtest_output=json:${{ inputs.reportdir }}assembly_gtest_results.json
|
||||
testLogFile: ${{ inputs.reportdir }}assembly_gtest_test_log.txt
|
||||
testName: Assembly
|
||||
- name: C++ core tests
|
||||
id: core
|
||||
- name: C++ app tests
|
||||
id: app
|
||||
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
|
||||
with:
|
||||
testCommand: ${{ inputs.builddir }}/tests/Tests_run --gtest_output=json:${{ inputs.reportdir }}core_gtest_results.json
|
||||
testLogFile: ${{ inputs.reportdir }}core_gtest_test_log.txt
|
||||
testName: Core
|
||||
testCommand: ${{ inputs.builddir }}/tests/App_tests_run --gtest_output=json:${{ inputs.reportdir }}app_gtest_results.json
|
||||
testLogFile: ${{ inputs.reportdir }}app_gtest_test_log.txt
|
||||
testName: App
|
||||
- name: C++ base tests
|
||||
id: base
|
||||
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
|
||||
with:
|
||||
testCommand: ${{ inputs.builddir }}/tests/Base_tests_run --gtest_output=json:${{ inputs.reportdir }}base_gtest_results.json
|
||||
testLogFile: ${{ inputs.reportdir }}base_gtest_test_log.txt
|
||||
testName: Base
|
||||
- name: C++ Gui tests
|
||||
id: gui
|
||||
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
|
||||
with:
|
||||
testCommand: ${{ inputs.builddir }}/tests/Gui_tests_run --gtest_output=json:${{ inputs.reportdir }}gui_gtest_results.json
|
||||
testLogFile: ${{ inputs.reportdir }}gui_gtest_test_log.txt
|
||||
testName: Gui
|
||||
- name: C++ Material tests
|
||||
id: material
|
||||
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
|
||||
@@ -82,6 +96,13 @@ runs:
|
||||
testCommand: ${{ inputs.builddir }}/tests/Mesh_tests_run --gtest_output=json:${{ inputs.reportdir }}meshpart_gtest_results.json
|
||||
testLogFile: ${{ inputs.reportdir }}meshpart_gtest_test_log.txt
|
||||
testName: MeshPart
|
||||
- name: C++ Misc tests
|
||||
id: misc
|
||||
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
|
||||
with:
|
||||
testCommand: ${{ inputs.builddir }}/tests/Misc_tests_run --gtest_output=json:${{ inputs.reportdir }}misc_gtest_results.json
|
||||
testLogFile: ${{ inputs.reportdir }}misc_gtest_test_log.txt
|
||||
testName: Misc
|
||||
- name: C++ Part tests
|
||||
id: part
|
||||
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
|
||||
|
||||
340
src/App/BackupPolicy.cpp
Normal file
340
src/App/BackupPolicy.cpp
Normal file
@@ -0,0 +1,340 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2020 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include <Base/TimeInfo.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/FileInfo.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Writer.h>
|
||||
|
||||
#include "BackupPolicy.h"
|
||||
|
||||
using namespace App;
|
||||
|
||||
void BackupPolicy::setPolicy(const Policy p)
|
||||
{
|
||||
policy = p;
|
||||
}
|
||||
void BackupPolicy::setNumberOfFiles(const int count)
|
||||
{
|
||||
numberOfFiles = count;
|
||||
}
|
||||
void BackupPolicy::useBackupExtension(const bool on)
|
||||
{
|
||||
useFCBakExtension = on;
|
||||
}
|
||||
void BackupPolicy::setDateFormat(const std::string& fmt)
|
||||
{
|
||||
saveBackupDateFormat = fmt;
|
||||
}
|
||||
void BackupPolicy::apply(const std::string& sourcename, const std::string& targetname)
|
||||
{
|
||||
switch (policy) {
|
||||
case Standard:
|
||||
applyStandard(sourcename, targetname);
|
||||
break;
|
||||
case TimeStamp:
|
||||
applyTimeStamp(sourcename, targetname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BackupPolicy::applyStandard(const std::string& sourcename, const std::string& targetname) const
|
||||
{
|
||||
// if saving the project data succeeded rename to the actual file name
|
||||
if (Base::FileInfo fi(targetname); fi.exists()) {
|
||||
if (numberOfFiles > 0) {
|
||||
int nSuff = 0;
|
||||
std::string fn = fi.fileName();
|
||||
Base::FileInfo di(fi.dirPath());
|
||||
std::vector<Base::FileInfo> backup;
|
||||
std::vector<Base::FileInfo> files = di.getDirectoryContent();
|
||||
for (const Base::FileInfo& it : files) {
|
||||
if (std::string file = it.fileName(); file.substr(0, fn.length()) == fn) {
|
||||
// starts with the same file name
|
||||
std::string suf(file.substr(fn.length()));
|
||||
if (!suf.empty()) {
|
||||
std::string::size_type nPos = suf.find_first_not_of("0123456789");
|
||||
if (nPos == std::string::npos) {
|
||||
// store all backup files
|
||||
backup.push_back(it);
|
||||
nSuff =
|
||||
std::max<int>(nSuff, static_cast<int>(std::atol(suf.c_str())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!backup.empty() && static_cast<int>(backup.size()) >= numberOfFiles) {
|
||||
// delete the oldest backup file we found
|
||||
Base::FileInfo del = backup.front();
|
||||
for (const Base::FileInfo& it : backup) {
|
||||
if (it.lastModified() < del.lastModified()) {
|
||||
del = it;
|
||||
}
|
||||
}
|
||||
|
||||
del.deleteFile();
|
||||
fn = del.filePath();
|
||||
}
|
||||
else {
|
||||
// create a new backup file
|
||||
std::stringstream str;
|
||||
str << fi.filePath() << (nSuff + 1);
|
||||
fn = str.str();
|
||||
}
|
||||
|
||||
if (!fi.renameFile(fn.c_str())) {
|
||||
Base::Console().warning("Cannot rename project file to backup file\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
fi.deleteFile();
|
||||
}
|
||||
}
|
||||
|
||||
if (Base::FileInfo tmp(sourcename); !tmp.renameFile(targetname.c_str())) {
|
||||
throw Base::FileException("Cannot rename tmp save file to project file",
|
||||
Base::FileInfo(targetname));
|
||||
}
|
||||
}
|
||||
|
||||
void BackupPolicy::applyTimeStamp(const std::string& sourcename, const std::string& targetname)
|
||||
{
|
||||
Base::FileInfo fi(targetname);
|
||||
|
||||
std::string fn = sourcename;
|
||||
std::string ext = fi.extension();
|
||||
std::string bn; // full path with no extension but with "."
|
||||
std::string pbn; // base name of the project + "."
|
||||
if (!ext.empty()) {
|
||||
bn = fi.filePath().substr(0, fi.filePath().length() - ext.length());
|
||||
pbn = fi.fileName().substr(0, fi.fileName().length() - ext.length());
|
||||
}
|
||||
else {
|
||||
bn = fi.filePath() + ".";
|
||||
pbn = fi.fileName() + ".";
|
||||
}
|
||||
|
||||
bool backupManagementError = false; // Note error and report at the end
|
||||
if (fi.exists()) {
|
||||
if (numberOfFiles > 0) {
|
||||
// replace . by - in format to avoid . between base name and extension
|
||||
boost::replace_all(saveBackupDateFormat, ".", "-");
|
||||
{
|
||||
// Remove all extra backups
|
||||
std::string filename = fi.fileName();
|
||||
Base::FileInfo di(fi.dirPath());
|
||||
std::vector<Base::FileInfo> backup;
|
||||
std::vector<Base::FileInfo> files = di.getDirectoryContent();
|
||||
for (const Base::FileInfo& it : files) {
|
||||
if (it.isFile()) {
|
||||
std::string file = it.fileName();
|
||||
std::string fext = it.extension();
|
||||
std::string fextUp = fext;
|
||||
std::transform(fextUp.begin(),
|
||||
fextUp.end(),
|
||||
fextUp.begin(),
|
||||
static_cast<int (*)(int)>(toupper));
|
||||
// re-enforcing identification of the backup file
|
||||
|
||||
|
||||
// old case : the name starts with the full name of the project and
|
||||
// follows with numbers
|
||||
if ((startsWith(file, filename) && (file.length() > filename.length())
|
||||
&& checkDigits(file.substr(filename.length())))
|
||||
||
|
||||
// .FCBak case : The bame starts with the base name of the project +
|
||||
// "."
|
||||
// + complement with no "." + ".FCBak"
|
||||
((fextUp == "FCBAK") && startsWith(file, pbn)
|
||||
&& (checkValidComplement(file, pbn, fext)))) {
|
||||
backup.push_back(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!backup.empty() && static_cast<int>(backup.size()) >= numberOfFiles) {
|
||||
std::sort(backup.begin(), backup.end(), fileComparisonByDate);
|
||||
// delete the oldest backup file we found
|
||||
// Base::FileInfo del = backup.front();
|
||||
int nb = 0;
|
||||
for (Base::FileInfo& it : backup) {
|
||||
nb++;
|
||||
if (nb >= numberOfFiles) {
|
||||
try {
|
||||
if (!it.deleteFile()) {
|
||||
backupManagementError = true;
|
||||
Base::Console().warning("Cannot remove backup file : %s\n",
|
||||
it.fileName().c_str());
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
backupManagementError = true;
|
||||
Base::Console().warning("Cannot remove backup file : %s\n",
|
||||
it.fileName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end remove backup
|
||||
|
||||
// create a new backup file
|
||||
{
|
||||
int ext2 = 1;
|
||||
if (useFCBakExtension) {
|
||||
std::stringstream str;
|
||||
Base::TimeInfo ti = fi.lastModified();
|
||||
time_t s = ti.getTime_t();
|
||||
struct tm* timeinfo = localtime(&s);
|
||||
char buffer[100];
|
||||
|
||||
strftime(buffer, sizeof(buffer), saveBackupDateFormat.c_str(), timeinfo);
|
||||
str << bn << buffer;
|
||||
|
||||
fn = str.str();
|
||||
bool done = false;
|
||||
|
||||
if ((fn.empty()) || (fn[fn.length() - 1] == ' ')
|
||||
|| (fn[fn.length() - 1] == '-')) {
|
||||
if (fn[fn.length() - 1] == ' ') {
|
||||
fn = fn.substr(0, fn.length() - 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!renameFileNoErase(fi, fn + ".FCBak")) {
|
||||
fn = fn + "-";
|
||||
}
|
||||
else {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
while (ext2 < numberOfFiles + 10) {
|
||||
if (renameFileNoErase(fi, fn + std::to_string(ext2) + ".FCBak")) {
|
||||
break;
|
||||
}
|
||||
ext2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// changed but simpler and solves also the delay sometimes introduced by
|
||||
// google drive
|
||||
while (ext2 < numberOfFiles + 10) {
|
||||
// linux just replace the file if exists, and then the existence is to
|
||||
// be tested before rename
|
||||
if (renameFileNoErase(fi, fi.filePath() + std::to_string(ext2))) {
|
||||
break;
|
||||
}
|
||||
ext2++;
|
||||
}
|
||||
}
|
||||
|
||||
if (ext2 >= numberOfFiles + 10) {
|
||||
Base::Console().error(
|
||||
"File not saved: Cannot rename project file to backup file\n");
|
||||
// throw Base::FileException("File not saved: Cannot rename project file to
|
||||
// backup file", fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
fi.deleteFile();
|
||||
}
|
||||
catch (...) {
|
||||
Base::Console().warning("Cannot remove backup file: %s\n",
|
||||
fi.fileName().c_str());
|
||||
backupManagementError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Base::FileInfo tmp(sourcename);
|
||||
if (!tmp.renameFile(targetname.c_str())) {
|
||||
throw Base::FileException(
|
||||
"Save interrupted: Cannot rename temporary file to project file",
|
||||
tmp);
|
||||
}
|
||||
|
||||
if (backupManagementError) {
|
||||
throw Base::FileException(
|
||||
"Warning: Save complete, but error while managing backup history.",
|
||||
fi);
|
||||
}
|
||||
}
|
||||
|
||||
bool BackupPolicy::fileComparisonByDate(const Base::FileInfo& i, const Base::FileInfo& j)
|
||||
{
|
||||
return (i.lastModified() > j.lastModified());
|
||||
}
|
||||
|
||||
bool BackupPolicy::startsWith(const std::string& st1, const std::string& st2) const
|
||||
{
|
||||
return st1.substr(0, st2.length()) == st2;
|
||||
}
|
||||
|
||||
bool BackupPolicy::checkValidString(const std::string& cmpl, const boost::regex& e) const
|
||||
{
|
||||
boost::smatch what;
|
||||
const bool res = boost::regex_search(cmpl, what, e);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool BackupPolicy::checkValidComplement(const std::string& file,
|
||||
const std::string& pbn,
|
||||
const std::string& ext) const
|
||||
{
|
||||
const std::string cmpl =
|
||||
file.substr(pbn.length(), file.length() - pbn.length() - ext.length() - 1);
|
||||
const boost::regex e(R"(^[^.]*$)");
|
||||
return checkValidString(cmpl, e);
|
||||
}
|
||||
|
||||
bool BackupPolicy::checkDigits(const std::string& cmpl) const
|
||||
{
|
||||
const boost::regex e(R"(^[0-9]*$)");
|
||||
return checkValidString(cmpl, e);
|
||||
}
|
||||
|
||||
bool BackupPolicy::renameFileNoErase(Base::FileInfo fi, const std::string& newName)
|
||||
{
|
||||
// linux just replaces the file if it exists, so the existence is to be tested before rename
|
||||
const Base::FileInfo nf(newName);
|
||||
if (!nf.exists()) {
|
||||
return fi.renameFile(newName.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
68
src/App/BackupPolicy.h
Normal file
68
src/App/BackupPolicy.h
Normal file
@@ -0,0 +1,68 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2020 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "FCGlobal.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <string>
|
||||
#include <Base/FileInfo.h>
|
||||
|
||||
namespace App
|
||||
{
|
||||
// Helper class to handle different backup policies: originally a private class in Document.cpp,
|
||||
// and extracted for public access to enable direct testing since the logic involved is quite
|
||||
// complex.
|
||||
class AppExport BackupPolicy
|
||||
{
|
||||
public:
|
||||
enum Policy
|
||||
{
|
||||
Standard,
|
||||
TimeStamp
|
||||
};
|
||||
void setPolicy(const Policy p);
|
||||
void setNumberOfFiles(const int count);
|
||||
void useBackupExtension(const bool on);
|
||||
void setDateFormat(const std::string& fmt);
|
||||
void apply(const std::string& sourcename, const std::string& targetname);
|
||||
|
||||
private:
|
||||
void applyStandard(const std::string& sourcename, const std::string& targetname) const;
|
||||
void applyTimeStamp(const std::string& sourcename, const std::string& targetname);
|
||||
static bool fileComparisonByDate(const Base::FileInfo& i, const Base::FileInfo& j);
|
||||
bool startsWith(const std::string& st1, const std::string& st2) const;
|
||||
bool checkValidString(const std::string& cmpl, const boost::regex& e) const;
|
||||
bool checkValidComplement(const std::string& file,
|
||||
const std::string& pbn,
|
||||
const std::string& ext) const;
|
||||
bool checkDigits(const std::string& cmpl) const;
|
||||
bool renameFileNoErase(Base::FileInfo fi, const std::string& newName);
|
||||
|
||||
private:
|
||||
Policy policy {Standard};
|
||||
int numberOfFiles {1};
|
||||
bool useFCBakExtension {true};
|
||||
std::string saveBackupDateFormat {"%Y%m%d-%H%M%S"};
|
||||
};
|
||||
} // namespace App
|
||||
@@ -133,6 +133,7 @@ SOURCE_GROUP("Pyi" FILES ${FreeCADApp_Pyi_SRCS})
|
||||
# The document stuff
|
||||
SET(Document_CPP_SRCS
|
||||
Annotation.cpp
|
||||
BackupPolicy.cpp
|
||||
Document.cpp
|
||||
DocumentObject.cpp
|
||||
Extension.cpp
|
||||
@@ -187,6 +188,7 @@ SET(Document_CPP_SRCS
|
||||
|
||||
SET(Document_HPP_SRCS
|
||||
Annotation.h
|
||||
BackupPolicy.h
|
||||
Document.h
|
||||
DocumentObject.h
|
||||
Extension.h
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
#include "private/DocumentP.h"
|
||||
#include "Application.h"
|
||||
#include "AutoTransaction.h"
|
||||
#include "BackupPolicy.h"
|
||||
#include "ExpressionParser.h"
|
||||
#include "GeoFeature.h"
|
||||
#include "License.h"
|
||||
@@ -1741,320 +1742,6 @@ bool Document::save()
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace App
|
||||
{
|
||||
// Helper class to handle different backup policies
|
||||
class BackupPolicy
|
||||
{
|
||||
public:
|
||||
enum Policy
|
||||
{
|
||||
Standard,
|
||||
TimeStamp
|
||||
};
|
||||
BackupPolicy()
|
||||
{}
|
||||
~BackupPolicy() = default;
|
||||
void setPolicy(const Policy p)
|
||||
{
|
||||
policy = p;
|
||||
}
|
||||
void setNumberOfFiles(const int count)
|
||||
{
|
||||
numberOfFiles = count;
|
||||
}
|
||||
void useBackupExtension(const bool on)
|
||||
{
|
||||
useFCBakExtension = on;
|
||||
}
|
||||
void setDateFormat(const std::string& fmt)
|
||||
{
|
||||
saveBackupDateFormat = fmt;
|
||||
}
|
||||
void apply(const std::string& sourcename, const std::string& targetname)
|
||||
{
|
||||
switch (policy) {
|
||||
case Standard:
|
||||
applyStandard(sourcename, targetname);
|
||||
break;
|
||||
case TimeStamp:
|
||||
applyTimeStamp(sourcename, targetname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void applyStandard(const std::string& sourcename, const std::string& targetname) const
|
||||
{
|
||||
// if saving the project data succeeded rename to the actual file name
|
||||
if (Base::FileInfo fi(targetname); fi.exists()) {
|
||||
if (numberOfFiles > 0) {
|
||||
int nSuff = 0;
|
||||
std::string fn = fi.fileName();
|
||||
Base::FileInfo di(fi.dirPath());
|
||||
std::vector<Base::FileInfo> backup;
|
||||
std::vector<Base::FileInfo> files = di.getDirectoryContent();
|
||||
for (const Base::FileInfo& it : files) {
|
||||
if (std::string file = it.fileName(); file.substr(0, fn.length()) == fn) {
|
||||
// starts with the same file name
|
||||
std::string suf(file.substr(fn.length()));
|
||||
if (!suf.empty()) {
|
||||
std::string::size_type nPos = suf.find_first_not_of("0123456789");
|
||||
if (nPos == std::string::npos) {
|
||||
// store all backup files
|
||||
backup.push_back(it);
|
||||
nSuff =
|
||||
std::max<int>(nSuff, static_cast<int>(std::atol(suf.c_str())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!backup.empty() && static_cast<int>(backup.size()) >= numberOfFiles) {
|
||||
// delete the oldest backup file we found
|
||||
Base::FileInfo del = backup.front();
|
||||
for (const Base::FileInfo& it : backup) {
|
||||
if (it.lastModified() < del.lastModified()) {
|
||||
del = it;
|
||||
}
|
||||
}
|
||||
|
||||
del.deleteFile();
|
||||
fn = del.filePath();
|
||||
}
|
||||
else {
|
||||
// create a new backup file
|
||||
std::stringstream str;
|
||||
str << fi.filePath() << (nSuff + 1);
|
||||
fn = str.str();
|
||||
}
|
||||
|
||||
if (!fi.renameFile(fn.c_str())) {
|
||||
Base::Console().warning("Cannot rename project file to backup file\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
fi.deleteFile();
|
||||
}
|
||||
}
|
||||
|
||||
if (Base::FileInfo tmp(sourcename); !tmp.renameFile(targetname.c_str())) {
|
||||
throw Base::FileException("Cannot rename tmp save file to project file",
|
||||
Base::FileInfo(targetname));
|
||||
}
|
||||
}
|
||||
void applyTimeStamp(const std::string& sourcename, const std::string& targetname)
|
||||
{
|
||||
Base::FileInfo fi(targetname);
|
||||
|
||||
std::string fn = sourcename;
|
||||
std::string ext = fi.extension();
|
||||
std::string bn; // full path with no extension but with "."
|
||||
std::string pbn; // base name of the project + "."
|
||||
if (!ext.empty()) {
|
||||
bn = fi.filePath().substr(0, fi.filePath().length() - ext.length());
|
||||
pbn = fi.fileName().substr(0, fi.fileName().length() - ext.length());
|
||||
}
|
||||
else {
|
||||
bn = fi.filePath() + ".";
|
||||
pbn = fi.fileName() + ".";
|
||||
}
|
||||
|
||||
bool backupManagementError = false; // Note error and report at the end
|
||||
if (fi.exists()) {
|
||||
if (numberOfFiles > 0) {
|
||||
// replace . by - in format to avoid . between base name and extension
|
||||
boost::replace_all(saveBackupDateFormat, ".", "-");
|
||||
{
|
||||
// Remove all extra backups
|
||||
std::string filename = fi.fileName();
|
||||
Base::FileInfo di(fi.dirPath());
|
||||
std::vector<Base::FileInfo> backup;
|
||||
std::vector<Base::FileInfo> files = di.getDirectoryContent();
|
||||
for (const Base::FileInfo& it : files) {
|
||||
if (it.isFile()) {
|
||||
std::string file = it.fileName();
|
||||
std::string fext = it.extension();
|
||||
std::string fextUp = fext;
|
||||
std::transform(fextUp.begin(),
|
||||
fextUp.end(),
|
||||
fextUp.begin(),
|
||||
static_cast<int (*)(int)>(toupper));
|
||||
// re-enforcing identification of the backup file
|
||||
|
||||
|
||||
// old case : the name starts with the full name of the project and
|
||||
// follows with numbers
|
||||
if ((startsWith(file, filename) && (file.length() > filename.length())
|
||||
&& checkDigits(file.substr(filename.length())))
|
||||
||
|
||||
// .FCBak case : The bame starts with the base name of the project +
|
||||
// "."
|
||||
// + complement with no "." + ".FCBak"
|
||||
((fextUp == "FCBAK") && startsWith(file, pbn)
|
||||
&& (checkValidComplement(file, pbn, fext)))) {
|
||||
backup.push_back(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!backup.empty() && static_cast<int>(backup.size()) >= numberOfFiles) {
|
||||
std::sort(backup.begin(), backup.end(), fileComparisonByDate);
|
||||
// delete the oldest backup file we found
|
||||
// Base::FileInfo del = backup.front();
|
||||
int nb = 0;
|
||||
for (Base::FileInfo& it : backup) {
|
||||
nb++;
|
||||
if (nb >= numberOfFiles) {
|
||||
try {
|
||||
if (!it.deleteFile()) {
|
||||
backupManagementError = true;
|
||||
Base::Console().warning("Cannot remove backup file : %s\n",
|
||||
it.fileName().c_str());
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
backupManagementError = true;
|
||||
Base::Console().warning("Cannot remove backup file : %s\n",
|
||||
it.fileName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end remove backup
|
||||
|
||||
// create a new backup file
|
||||
{
|
||||
int ext2 = 1;
|
||||
if (useFCBakExtension) {
|
||||
std::stringstream str;
|
||||
Base::TimeInfo ti = fi.lastModified();
|
||||
time_t s = ti.getTime_t();
|
||||
struct tm* timeinfo = localtime(&s);
|
||||
char buffer[100];
|
||||
|
||||
strftime(buffer, sizeof(buffer), saveBackupDateFormat.c_str(), timeinfo);
|
||||
str << bn << buffer;
|
||||
|
||||
fn = str.str();
|
||||
bool done = false;
|
||||
|
||||
if ((fn.empty()) || (fn[fn.length() - 1] == ' ')
|
||||
|| (fn[fn.length() - 1] == '-')) {
|
||||
if (fn[fn.length() - 1] == ' ') {
|
||||
fn = fn.substr(0, fn.length() - 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!renameFileNoErase(fi, fn + ".FCBak")) {
|
||||
fn = fn + "-";
|
||||
}
|
||||
else {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
while (ext2 < numberOfFiles + 10) {
|
||||
if (renameFileNoErase(fi, fn + std::to_string(ext2) + ".FCBak")) {
|
||||
break;
|
||||
}
|
||||
ext2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// changed but simpler and solves also the delay sometimes introduced by
|
||||
// google drive
|
||||
while (ext2 < numberOfFiles + 10) {
|
||||
// linux just replace the file if exists, and then the existence is to
|
||||
// be tested before rename
|
||||
if (renameFileNoErase(fi, fi.filePath() + std::to_string(ext2))) {
|
||||
break;
|
||||
}
|
||||
ext2++;
|
||||
}
|
||||
}
|
||||
|
||||
if (ext2 >= numberOfFiles + 10) {
|
||||
Base::Console().error(
|
||||
"File not saved: Cannot rename project file to backup file\n");
|
||||
// throw Base::FileException("File not saved: Cannot rename project file to
|
||||
// backup file", fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
fi.deleteFile();
|
||||
}
|
||||
catch (...) {
|
||||
Base::Console().warning("Cannot remove backup file: %s\n",
|
||||
fi.fileName().c_str());
|
||||
backupManagementError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Base::FileInfo tmp(sourcename);
|
||||
if (!tmp.renameFile(targetname.c_str())) {
|
||||
throw Base::FileException(
|
||||
"Save interrupted: Cannot rename temporary file to project file",
|
||||
tmp);
|
||||
}
|
||||
|
||||
if (backupManagementError) {
|
||||
throw Base::FileException(
|
||||
"Warning: Save complete, but error while managing backup history.",
|
||||
fi);
|
||||
}
|
||||
}
|
||||
static bool fileComparisonByDate(const Base::FileInfo& i, const Base::FileInfo& j)
|
||||
{
|
||||
return (i.lastModified() > j.lastModified());
|
||||
}
|
||||
bool startsWith(const std::string& st1, const std::string& st2) const
|
||||
{
|
||||
return st1.substr(0, st2.length()) == st2;
|
||||
}
|
||||
bool checkValidString(const std::string& cmpl, const boost::regex& e) const
|
||||
{
|
||||
boost::smatch what;
|
||||
const bool res = boost::regex_search(cmpl, what, e);
|
||||
return res;
|
||||
}
|
||||
bool checkValidComplement(const std::string& file,
|
||||
const std::string& pbn,
|
||||
const std::string& ext) const
|
||||
{
|
||||
const std::string cmpl =
|
||||
file.substr(pbn.length(), file.length() - pbn.length() - ext.length() - 1);
|
||||
const boost::regex e(R"(^[^.]*$)");
|
||||
return checkValidString(cmpl, e);
|
||||
}
|
||||
bool checkDigits(const std::string& cmpl) const
|
||||
{
|
||||
const boost::regex e(R"(^[0-9]*$)");
|
||||
return checkValidString(cmpl, e);
|
||||
}
|
||||
bool renameFileNoErase(Base::FileInfo fi, const std::string& newName)
|
||||
{
|
||||
// linux just replaces the file if it exists, so the existence is to be tested before rename
|
||||
const Base::FileInfo nf(newName);
|
||||
if (!nf.exists()) {
|
||||
return fi.renameFile(newName.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
Policy policy {Standard};
|
||||
int numberOfFiles {1};
|
||||
bool useFCBakExtension {true};
|
||||
std::string saveBackupDateFormat {"%Y%m%d-%H%M%S"};
|
||||
};
|
||||
} // namespace App
|
||||
|
||||
bool Document::saveToFile(const char* filename) const
|
||||
{
|
||||
signalStartSave(*this, filename);
|
||||
|
||||
@@ -41,7 +41,7 @@ if(MSVC)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DCOIN_DLL -D_USE_MATH_DEFINES)
|
||||
add_definitions(-DCOIN_DLL)
|
||||
endif(WIN32)
|
||||
|
||||
if(NOT BUILD_DYNAMIC_LINK_PYTHON)
|
||||
@@ -78,69 +78,83 @@ function(setup_qt_test)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Add test executables here
|
||||
|
||||
set(TestExecutables
|
||||
Tests_run
|
||||
App_tests_run
|
||||
Base_tests_run
|
||||
Misc_tests_run
|
||||
Zipios_tests_run
|
||||
)
|
||||
|
||||
# NOTE: The following tests don't yet run on Windows because they can't find the *.pyd files needed by each FreeCAD
|
||||
# module.
|
||||
|
||||
if(BUILD_GUI)
|
||||
list (APPEND TestExecutables Gui_tests_run)
|
||||
endif()
|
||||
if(BUILD_ASSEMBLY)
|
||||
list (APPEND TestExecutables Assembly_tests_run)
|
||||
list (APPEND TestExecutables Assembly_tests_run)
|
||||
endif(BUILD_ASSEMBLY)
|
||||
if(BUILD_MATERIAL)
|
||||
list (APPEND TestExecutables Material_tests_run)
|
||||
list (APPEND TestExecutables Material_tests_run)
|
||||
endif(BUILD_MATERIAL)
|
||||
if(BUILD_MEASURE)
|
||||
list (APPEND TestExecutables Measure_tests_run)
|
||||
list (APPEND TestExecutables Measure_tests_run)
|
||||
endif(BUILD_MEASURE)
|
||||
if(BUILD_MESH)
|
||||
list (APPEND TestExecutables Mesh_tests_run)
|
||||
list (APPEND TestExecutables Mesh_tests_run)
|
||||
endif(BUILD_MESH)
|
||||
if(BUILD_MESH_PART)
|
||||
list (APPEND TestExecutables MeshPart_tests_run)
|
||||
list (APPEND TestExecutables MeshPart_tests_run)
|
||||
endif(BUILD_MESH_PART)
|
||||
if(BUILD_PART)
|
||||
list (APPEND TestExecutables Part_tests_run)
|
||||
list (APPEND TestExecutables Part_tests_run)
|
||||
endif(BUILD_PART)
|
||||
if(BUILD_PART_DESIGN)
|
||||
list (APPEND TestExecutables PartDesign_tests_run)
|
||||
endif(BUILD_PART_DESIGN)
|
||||
if(BUILD_POINTS)
|
||||
list (APPEND TestExecutables Points_tests_run)
|
||||
list (APPEND TestExecutables Points_tests_run)
|
||||
endif(BUILD_POINTS)
|
||||
if(BUILD_SKETCHER)
|
||||
list (APPEND TestExecutables Sketcher_tests_run)
|
||||
list (APPEND TestExecutables Sketcher_tests_run)
|
||||
endif(BUILD_SKETCHER)
|
||||
if(BUILD_SPREADSHEET)
|
||||
list (APPEND TestExecutables Spreadsheet_tests_run)
|
||||
list (APPEND TestExecutables Spreadsheet_tests_run)
|
||||
endif()
|
||||
if(BUILD_START)
|
||||
list (APPEND TestExecutables Start_tests_run)
|
||||
list (APPEND TestExecutables Start_tests_run)
|
||||
endif()
|
||||
|
||||
# -------------------------
|
||||
|
||||
foreach (exe ${TestExecutables})
|
||||
add_executable(${exe})
|
||||
endforeach()
|
||||
|
||||
if ( NOT FREECAD_USE_EXTERNAL_GTEST )
|
||||
if(WIN32)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
endif()
|
||||
add_subdirectory(lib)
|
||||
endif()
|
||||
add_subdirectory(src)
|
||||
|
||||
target_link_libraries(Tests_run
|
||||
gtest_main
|
||||
gmock_main
|
||||
${Google_Tests_LIBS}
|
||||
FreeCADApp
|
||||
FreeCADGui
|
||||
)
|
||||
|
||||
include(GoogleTest)
|
||||
# discovers tests by asking the compiled test executable to enumerate its tests
|
||||
set(CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE PRE_TEST)
|
||||
|
||||
foreach (exe ${TestExecutables})
|
||||
if(WIN32)
|
||||
# On Windows the test executables need to be in the same place as all the other DLLs that are getting built
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
# Visual Studio solution file, supports switching configs on the fly in the IDE
|
||||
set(OUTPUT_DIR ${CMAKE_BINARY_DIR}/bin)
|
||||
foreach(OUTPUT_CONFIG Debug Release RelWithDebInfo MinSizeRel)
|
||||
string(TOUPPER "${OUTPUT_CONFIG}" UPPER_CONFIG)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${UPPER_CONFIG} ${OUTPUT_DIR}/${OUTPUT_CONFIG})
|
||||
endforeach()
|
||||
else()
|
||||
# Ninja (usually), e.g. when using CLion with MSVC toolchain, etc. is actually single-config
|
||||
set_target_properties(${exe} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set_target_properties(${exe} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
endif()
|
||||
else()
|
||||
set_target_properties(${exe} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests)
|
||||
endif()
|
||||
gtest_discover_tests(${exe})
|
||||
endforeach()
|
||||
|
||||
114
tests/src/App/BackupPolicy.cpp
Normal file
114
tests/src/App/BackupPolicy.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2025 The FreeCAD project association AISBL *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "InitApplication.h"
|
||||
|
||||
#include <App/BackupPolicy.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
|
||||
class BackupPolicyTest: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestSuite()
|
||||
{
|
||||
tests::initApplication();
|
||||
}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
_tempDir =
|
||||
std::filesystem::temp_directory_path() / ("fc_backup_policy-" + randomString(16));
|
||||
std::filesystem::create_directory(_tempDir);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
std::filesystem::remove_all(_tempDir);
|
||||
}
|
||||
|
||||
void apply(const std::string& sourcename, const std::string& targetname)
|
||||
{
|
||||
_policy.apply(sourcename, targetname);
|
||||
}
|
||||
|
||||
void setPolicyTerms(App::BackupPolicy::Policy p, int count, bool useExt, const std::string& fmt)
|
||||
{
|
||||
_policy.setPolicy(p);
|
||||
_policy.setNumberOfFiles(count);
|
||||
_policy.useBackupExtension(useExt);
|
||||
_policy.setDateFormat(fmt);
|
||||
}
|
||||
|
||||
// Create a named temporary file: returns the full path to the new file. Deleted by the TearDown
|
||||
// method at the end of the test.
|
||||
std::filesystem::path createTempFile(const std::string& filename)
|
||||
{
|
||||
std::filesystem::path p = _tempDir / filename;
|
||||
std::ofstream fileStream(p.string());
|
||||
fileStream << "Test data";
|
||||
fileStream.close();
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::string randomString(size_t length)
|
||||
{
|
||||
static constexpr std::string_view chars = "0123456789"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> dis(0, static_cast<int>(chars.size()) - 1);
|
||||
|
||||
std::string result;
|
||||
result.reserve(length);
|
||||
|
||||
std::ranges::generate_n(std::back_inserter(result), length, [&]() {
|
||||
return chars[dis(gen)];
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
App::BackupPolicy _policy;
|
||||
std::filesystem::path _tempDir;
|
||||
};
|
||||
|
||||
TEST_F(BackupPolicyTest, StandardSourceDoesNotExist)
|
||||
{
|
||||
// Arrange
|
||||
setPolicyTerms(App::BackupPolicy::Policy::Standard, 1, true, "%Y-%m-%d_%H-%M-%S");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_THROW(apply("nonexistent.fcstd", "backup.fcstd"), Base::FileException);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
target_compile_definitions(Tests_run PRIVATE DATADIR="${CMAKE_SOURCE_DIR}/data")
|
||||
|
||||
target_sources(Tests_run PRIVATE
|
||||
add_executable(App_tests_run
|
||||
Application.cpp
|
||||
BackupPolicy.cpp
|
||||
Branding.cpp
|
||||
ComplexGeoData.cpp
|
||||
Document.cpp
|
||||
@@ -24,3 +23,12 @@ target_sources(Tests_run PRIVATE
|
||||
VarSet.cpp
|
||||
VRMLObject.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(App_tests_run PRIVATE DATADIR="${CMAKE_SOURCE_DIR}/data")
|
||||
|
||||
target_link_libraries(App_tests_run PRIVATE
|
||||
GTest::gtest_main
|
||||
GTest::gmock_main
|
||||
${Google_Tests_LIBS}
|
||||
FreeCADApp
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
target_sources(Tests_run PRIVATE
|
||||
add_executable(Base_tests_run
|
||||
Axis.cpp
|
||||
Base64.cpp
|
||||
Bitmask.cpp
|
||||
@@ -31,3 +31,10 @@ target_sources(Tests_run PRIVATE
|
||||
)
|
||||
|
||||
setup_qt_test(InventorBuilder)
|
||||
|
||||
target_link_libraries(Base_tests_run PRIVATE
|
||||
GTest::gtest_main
|
||||
GTest::gmock_main
|
||||
${Google_Tests_LIBS}
|
||||
FreeCADApp
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Standard C++ GTest tests
|
||||
target_sources(Tests_run PRIVATE
|
||||
add_executable(Gui_tests_run
|
||||
Assistant.cpp
|
||||
Camera.cpp
|
||||
StyleParameters/StyleParametersApplicationTest.cpp
|
||||
@@ -9,3 +9,12 @@ target_sources(Tests_run PRIVATE
|
||||
|
||||
# Qt tests
|
||||
setup_qt_test(QuantitySpinBox)
|
||||
|
||||
|
||||
target_link_libraries(Gui_tests_run PRIVATE
|
||||
GTest::gtest_main
|
||||
GTest::gmock_main
|
||||
${Google_Tests_LIBS}
|
||||
FreeCADApp
|
||||
FreeCADGui
|
||||
)
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
target_sources(Tests_run PRIVATE
|
||||
add_executable(Misc_tests_run
|
||||
fmt.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(Misc_tests_run PRIVATE
|
||||
GTest::gtest_main
|
||||
GTest::gmock_main
|
||||
${Google_Tests_LIBS}
|
||||
fmt::fmt
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "fmt/format.h"
|
||||
#include "fmt/printf.h"
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/printf.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
target_sources(Assembly_tests_run PRIVATE
|
||||
add_executable(Assembly_tests_run
|
||||
AssemblyObject.cpp
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
if (NOT FREECAD_USE_EXTERNAL_ONDSELSOLVER)
|
||||
target_include_directories(Assembly_tests_run PUBLIC
|
||||
@@ -10,5 +11,3 @@ target_link_libraries(Assembly_tests_run
|
||||
${Google_Tests_LIBS}
|
||||
Assembly
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
target_sources(Material_tests_run PRIVATE
|
||||
add_executable(Material_tests_run
|
||||
TestMaterialCards.cpp
|
||||
TestMaterialFilter.cpp
|
||||
TestMaterialProperties.cpp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
target_link_libraries(Material_tests_run
|
||||
gtest_main
|
||||
${Google_Tests_LIBS}
|
||||
Materials
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
target_sources(Measure_tests_run PRIVATE
|
||||
add_executable(Measure_tests_run
|
||||
MeasureDistance.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
target_link_libraries(Measure_tests_run
|
||||
gtest_main
|
||||
${Google_Tests_LIBS}
|
||||
Measure
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
target_compile_definitions(Mesh_tests_run PRIVATE DATADIR="${CMAKE_SOURCE_DIR}/data")
|
||||
|
||||
target_sources(Mesh_tests_run PRIVATE
|
||||
add_executable(Mesh_tests_run
|
||||
Core/KDTree.cpp
|
||||
Exporter.cpp
|
||||
Importer.cpp
|
||||
Mesh.cpp
|
||||
MeshFeature.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(Mesh_tests_run PRIVATE DATADIR="${CMAKE_SOURCE_DIR}/data")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
target_link_libraries(Mesh_tests_run
|
||||
gtest_main
|
||||
${Google_Tests_LIBS}
|
||||
Mesh
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
target_sources(MeshPart_tests_run PRIVATE
|
||||
add_executable(MeshPart_tests_run
|
||||
MeshPart.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
target_link_libraries(MeshPart_tests_run
|
||||
gtest_main
|
||||
${Google_Tests_LIBS}
|
||||
MeshPart
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
target_sources(Part_tests_run PRIVATE
|
||||
add_executable(Part_tests_run
|
||||
Attacher.cpp
|
||||
AttachExtension.cpp
|
||||
BRepMesh.cpp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
target_link_libraries(Part_tests_run
|
||||
gtest_main
|
||||
${Google_Tests_LIBS}
|
||||
Part
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
target_sources(PartDesign_tests_run PRIVATE
|
||||
add_executable(PartDesign_tests_run
|
||||
BackwardCompatibility.cpp
|
||||
DatumPlane.cpp
|
||||
ShapeBinder.cpp
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
target_link_libraries(PartDesign_tests_run
|
||||
gtest_main
|
||||
${Google_Tests_LIBS}
|
||||
PartDesign
|
||||
Sketcher
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
target_sources(Points_tests_run PRIVATE
|
||||
add_executable(Points_tests_run
|
||||
Points.cpp
|
||||
PointsFeature.cpp
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
target_link_libraries(Points_tests_run
|
||||
gtest_main
|
||||
${Google_Tests_LIBS}
|
||||
Points
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
target_sources(Sketcher_tests_run PRIVATE
|
||||
add_executable(Sketcher_tests_run
|
||||
SketcherTestHelpers.cpp
|
||||
SketchObject.cpp
|
||||
SketchObjectChanges.cpp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
target_link_libraries(Sketcher_tests_run
|
||||
gtest_main
|
||||
${Google_Tests_LIBS}
|
||||
Sketcher
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
target_sources(Spreadsheet_tests_run PRIVATE
|
||||
add_executable(Spreadsheet_tests_run
|
||||
PropertySheet.cpp
|
||||
RenameProperty.cpp
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
target_link_libraries(Spreadsheet_tests_run
|
||||
gtest_main
|
||||
${Google_Tests_LIBS}
|
||||
Spreadsheet
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
target_sources(
|
||||
Start_tests_run
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/FileUtilities.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ThumbnailSource.cpp
|
||||
add_executable(Start_tests_run
|
||||
FileUtilities.cpp
|
||||
ThumbnailSource.cpp
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
add_subdirectory(App)
|
||||
|
||||
target_include_directories(Start_tests_run PUBLIC
|
||||
${Python3_INCLUDE_DIRS}
|
||||
@@ -8,5 +9,3 @@ target_link_libraries(Start_tests_run
|
||||
${Google_Tests_LIBS}
|
||||
Start
|
||||
)
|
||||
|
||||
add_subdirectory(App)
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
target_sources(
|
||||
Tests_run
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/collectioncollection.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zipfile.cpp
|
||||
add_executable(Zipios_tests_run
|
||||
collectioncollection.cpp
|
||||
zipfile.cpp
|
||||
)
|
||||
|
||||
|
||||
target_link_libraries(Zipios_tests_run PRIVATE
|
||||
GTest::gtest_main
|
||||
GTest::gmock_main
|
||||
${Google_Tests_LIBS}
|
||||
FreeCADApp
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
Zipios_tests_run PRIVATE
|
||||
${ZIPIOS_INCLUDES}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user