mirror of
https://github.com/etlegacy/Update-Installer.git
synced 2025-01-22 07:21:13 +00:00
Support parsing update scripts structured for backwards compatibility with Mendeley Desktop clients <= 1.0
* Support XML scripts where the V3 script is wrapped in an <update-v3> element. * Add some basic unit testing facilities - if we find that something more sophisticated is needed, we can look at using cppunit, googletest or similar. * Add a test that parsing the same script with both the old and the new structure yields equivalent UpdateScript instances.
This commit is contained in:
parent
c1a5cce3da
commit
a280b5d3d1
8 changed files with 246 additions and 45 deletions
|
@ -14,7 +14,6 @@ set (SOURCES
|
|||
UpdateInstaller.cpp
|
||||
UpdateScript.cpp
|
||||
UpdaterOptions.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set (HEADERS
|
||||
|
@ -27,12 +26,12 @@ set (HEADERS
|
|||
UpdaterOptions.h
|
||||
)
|
||||
|
||||
add_executable(updater
|
||||
add_library(updatershared
|
||||
${SOURCES}
|
||||
${HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries(updater
|
||||
target_link_libraries(updatershared
|
||||
anyoption
|
||||
tinyxml
|
||||
minizip
|
||||
|
@ -41,5 +40,13 @@ target_link_libraries(updater
|
|||
/usr/lib/libboost_thread.a
|
||||
)
|
||||
|
||||
add_executable(updater
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(updater
|
||||
updatershared
|
||||
)
|
||||
|
||||
install(TARGETS updater RUNTIME DESTINATION bin)
|
||||
|
||||
|
|
|
@ -19,50 +19,17 @@ void UpdateScript::parse(const std::string& path)
|
|||
LOG(Info,"Loaded script from " + path);
|
||||
|
||||
const TiXmlElement* updateNode = document.RootElement();
|
||||
|
||||
const TiXmlElement* depsNode = updateNode->FirstChildElement("dependencies");
|
||||
const TiXmlElement* depFileNode = depsNode->FirstChildElement("file");
|
||||
while (depFileNode)
|
||||
const TiXmlElement* v3UpdateNode = updateNode->FirstChildElement("update-v3");
|
||||
if (v3UpdateNode)
|
||||
{
|
||||
m_dependencies.push_back(std::string(depFileNode->GetText()));
|
||||
depFileNode = depFileNode->NextSiblingElement("file");
|
||||
// this XML file is structured for backwards compatibility
|
||||
// with Mendeley Desktop <= 1.0 clients. The normal update XML contents
|
||||
// are wrapped in an <update-v3> node.
|
||||
parseUpdate(v3UpdateNode);
|
||||
}
|
||||
LOG(Info,"Dependency count " + intToStr(m_dependencies.size()));
|
||||
|
||||
const TiXmlElement* installNode = updateNode->FirstChildElement("install");
|
||||
if (installNode)
|
||||
else
|
||||
{
|
||||
const TiXmlElement* installFileNode = installNode->FirstChildElement("file");
|
||||
while (installFileNode)
|
||||
{
|
||||
m_filesToInstall.push_back(parseFile(installFileNode));
|
||||
installFileNode = installFileNode->NextSiblingElement("file");
|
||||
}
|
||||
LOG(Info,"Files to install count " + intToStr(m_filesToInstall.size()));
|
||||
}
|
||||
|
||||
const TiXmlElement* uninstallNode = updateNode->FirstChildElement("uninstall");
|
||||
if (uninstallNode)
|
||||
{
|
||||
const TiXmlElement* uninstallFileNode = uninstallNode->FirstChildElement("file");
|
||||
while (uninstallFileNode)
|
||||
{
|
||||
m_filesToUninstall.push_back(uninstallFileNode->GetText());
|
||||
uninstallFileNode = uninstallFileNode->NextSiblingElement("file");
|
||||
}
|
||||
LOG(Info,"Files to uninstall count " + intToStr(m_filesToUninstall.size()));
|
||||
}
|
||||
|
||||
const TiXmlElement* packagesNode = updateNode->FirstChildElement("packages");
|
||||
if (packagesNode)
|
||||
{
|
||||
const TiXmlElement* packageNode = packagesNode->FirstChildElement("package");
|
||||
while (packageNode)
|
||||
{
|
||||
m_packages.push_back(parsePackage(packageNode));
|
||||
packageNode = packageNode->NextSiblingElement("package");
|
||||
}
|
||||
LOG(Info,"Package count " + intToStr(m_packages.size()));
|
||||
parseUpdate(updateNode);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -71,6 +38,54 @@ void UpdateScript::parse(const std::string& path)
|
|||
}
|
||||
}
|
||||
|
||||
void UpdateScript::parseUpdate(const TiXmlElement* updateNode)
|
||||
{
|
||||
const TiXmlElement* depsNode = updateNode->FirstChildElement("dependencies");
|
||||
const TiXmlElement* depFileNode = depsNode->FirstChildElement("file");
|
||||
while (depFileNode)
|
||||
{
|
||||
m_dependencies.push_back(std::string(depFileNode->GetText()));
|
||||
depFileNode = depFileNode->NextSiblingElement("file");
|
||||
}
|
||||
LOG(Info,"Dependency count " + intToStr(m_dependencies.size()));
|
||||
|
||||
const TiXmlElement* installNode = updateNode->FirstChildElement("install");
|
||||
if (installNode)
|
||||
{
|
||||
const TiXmlElement* installFileNode = installNode->FirstChildElement("file");
|
||||
while (installFileNode)
|
||||
{
|
||||
m_filesToInstall.push_back(parseFile(installFileNode));
|
||||
installFileNode = installFileNode->NextSiblingElement("file");
|
||||
}
|
||||
LOG(Info,"Files to install count " + intToStr(m_filesToInstall.size()));
|
||||
}
|
||||
|
||||
const TiXmlElement* uninstallNode = updateNode->FirstChildElement("uninstall");
|
||||
if (uninstallNode)
|
||||
{
|
||||
const TiXmlElement* uninstallFileNode = uninstallNode->FirstChildElement("file");
|
||||
while (uninstallFileNode)
|
||||
{
|
||||
m_filesToUninstall.push_back(uninstallFileNode->GetText());
|
||||
uninstallFileNode = uninstallFileNode->NextSiblingElement("file");
|
||||
}
|
||||
LOG(Info,"Files to uninstall count " + intToStr(m_filesToUninstall.size()));
|
||||
}
|
||||
|
||||
const TiXmlElement* packagesNode = updateNode->FirstChildElement("packages");
|
||||
if (packagesNode)
|
||||
{
|
||||
const TiXmlElement* packageNode = packagesNode->FirstChildElement("package");
|
||||
while (packageNode)
|
||||
{
|
||||
m_packages.push_back(parsePackage(packageNode));
|
||||
packageNode = packageNode->NextSiblingElement("package");
|
||||
}
|
||||
LOG(Info,"Package count " + intToStr(m_packages.size()));
|
||||
}
|
||||
}
|
||||
|
||||
std::string elementText(const TiXmlElement* element)
|
||||
{
|
||||
if (!element)
|
||||
|
|
|
@ -16,6 +16,14 @@ class UpdateScriptPackage
|
|||
std::string sha1;
|
||||
std::string source;
|
||||
int size;
|
||||
|
||||
bool operator==(const UpdateScriptPackage& other) const
|
||||
{
|
||||
return name == other.name &&
|
||||
sha1 == other.sha1 &&
|
||||
source == other.source &&
|
||||
size == other.size;
|
||||
}
|
||||
};
|
||||
|
||||
class UpdateScriptFile
|
||||
|
@ -29,6 +37,14 @@ class UpdateScriptFile
|
|||
std::string package;
|
||||
int permissions;
|
||||
std::string linkTarget;
|
||||
|
||||
bool operator==(const UpdateScriptFile& other) const
|
||||
{
|
||||
return path == other.path &&
|
||||
package == other.package &&
|
||||
permissions == other.permissions &&
|
||||
linkTarget == other.linkTarget;
|
||||
}
|
||||
};
|
||||
|
||||
class UpdateScript
|
||||
|
@ -45,6 +61,7 @@ class UpdateScript
|
|||
const std::vector<std::string>& filesToUninstall() const;
|
||||
|
||||
private:
|
||||
void parseUpdate(const TiXmlElement* element);
|
||||
UpdateScriptFile parseFile(const TiXmlElement* element);
|
||||
UpdateScriptPackage parsePackage(const TiXmlElement* element);
|
||||
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
|
||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
|
||||
# Create helper binaries for unit tests
|
||||
add_executable(oldapp
|
||||
old_app.cpp
|
||||
)
|
||||
|
||||
add_executable(newapp
|
||||
new_app.cpp
|
||||
)
|
||||
|
||||
# Install data files required by unit tests
|
||||
set(TEST_FILES
|
||||
file_list.xml
|
||||
v2_file_list.xml
|
||||
test-update.rb
|
||||
)
|
||||
|
||||
|
@ -19,3 +23,12 @@ foreach(TEST_FILE ${TEST_FILES})
|
|||
)
|
||||
endforeach()
|
||||
|
||||
# Add unit test binaries
|
||||
add_executable(TestUpdateScript
|
||||
TestUpdateScript.cpp
|
||||
)
|
||||
target_link_libraries(TestUpdateScript
|
||||
updatershared
|
||||
)
|
||||
|
||||
|
||||
|
|
28
src/tests/TestUpdateScript.cpp
Normal file
28
src/tests/TestUpdateScript.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "TestUpdateScript.h"
|
||||
|
||||
#include "TestUtils.h"
|
||||
#include "UpdateScript.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void TestUpdateScript::testV2Script()
|
||||
{
|
||||
UpdateScript newFormat;
|
||||
UpdateScript oldFormat;
|
||||
|
||||
newFormat.parse("file_list.xml");
|
||||
oldFormat.parse("v2_file_list.xml");
|
||||
|
||||
TEST_COMPARE(newFormat.dependencies(),oldFormat.dependencies());
|
||||
TEST_COMPARE(newFormat.packages(),oldFormat.packages());
|
||||
TEST_COMPARE(newFormat.filesToInstall(),oldFormat.filesToInstall());
|
||||
TEST_COMPARE(newFormat.filesToUninstall(),oldFormat.filesToUninstall());
|
||||
}
|
||||
|
||||
int main(int,char**)
|
||||
{
|
||||
TestList<TestUpdateScript> tests;
|
||||
tests.addTest(&TestUpdateScript::testV2Script);
|
||||
return TestUtils::runTest(tests);
|
||||
}
|
||||
|
8
src/tests/TestUpdateScript.h
Normal file
8
src/tests/TestUpdateScript.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
class TestUpdateScript
|
||||
{
|
||||
public:
|
||||
void testV2Script();
|
||||
};
|
||||
|
68
src/tests/TestUtils.h
Normal file
68
src/tests/TestUtils.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
template <class T>
|
||||
class TestList
|
||||
{
|
||||
public:
|
||||
void addTest(void (T::*test)())
|
||||
{
|
||||
m_tests.push_back(std::mem_fun(test));
|
||||
}
|
||||
|
||||
int size() const
|
||||
{
|
||||
return m_tests.size();
|
||||
}
|
||||
|
||||
void runTest(T* testInstance, int i)
|
||||
{
|
||||
m_tests.at(i)(testInstance);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::mem_fun_t<void,T> > m_tests;
|
||||
};
|
||||
|
||||
class TestUtils
|
||||
{
|
||||
public:
|
||||
template <class X, class Y>
|
||||
static void compare(const X& x, const Y& y, const char* xString, const char* yString)
|
||||
{
|
||||
if (x != y)
|
||||
{
|
||||
throw "Actual and expected values differ. "
|
||||
"Actual: " + std::string(xString) +
|
||||
" Expected: " + std::string(yString);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static int runTest(class TestList<T>& tests) throw ()
|
||||
{
|
||||
try
|
||||
{
|
||||
T testInstance;
|
||||
for (int i=0; i < tests.size(); i++)
|
||||
{
|
||||
tests.runTest(&testInstance,i);
|
||||
}
|
||||
std::cout << "Test passed" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cout << "Test failed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define TEST_COMPARE(x,y) \
|
||||
TestUtils::compare(x,y,#x,#y);
|
||||
|
45
src/tests/v2_file_list.xml
Normal file
45
src/tests/v2_file_list.xml
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0"?>
|
||||
<update version="3">
|
||||
<!-- Update script designed for backwards compatibility with MD <= 1.0.
|
||||
The packages to download are listed as if they were ordinary files
|
||||
to install in a top-level <install> section.
|
||||
|
||||
The standard V3 version of the update XML format is then wrapped inside
|
||||
an <update-v3> element.
|
||||
!-->
|
||||
<update-v3>
|
||||
<targetVersion>2.0</targetVersion>
|
||||
<platform>Test</platform>
|
||||
<dependencies>
|
||||
<!-- The new updater is standalone and has no dependencies,
|
||||
except for standard system libraries.
|
||||
!-->
|
||||
</dependencies>
|
||||
<packages>
|
||||
<package>
|
||||
<name>app-pkg</name>
|
||||
<hash>$APP_PACKAGE_HASH</hash>
|
||||
<size>$APP_PACKAGE_SIZE</size>
|
||||
<source>http://some/dummy/URL</source>
|
||||
</package>
|
||||
</packages>
|
||||
<install>
|
||||
<file>
|
||||
<name>app</name>
|
||||
<hash>$UPDATED_APP_HASH</hash>
|
||||
<size>$UPDATED_APP_SIZE</size>
|
||||
<permissions>30549</permissions>
|
||||
<package>app-pkg</package>
|
||||
</file>
|
||||
<!-- Test symlink !-->
|
||||
<file>
|
||||
<name>test-dir/app-symlink</name>
|
||||
<target>../app</target>
|
||||
</file>
|
||||
</install>
|
||||
<uninstall>
|
||||
<!-- TODO - List some files to uninstall here !-->
|
||||
<file>file-to-uninstall.txt</file>
|
||||
</uninstall>
|
||||
</update-v3>
|
||||
</update>
|
Loading…
Reference in a new issue