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:
Robert Knight 2011-08-22 12:06:48 +01:00
parent c1a5cce3da
commit a280b5d3d1
8 changed files with 246 additions and 45 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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);

View file

@ -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
)

View 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);
}

View file

@ -0,0 +1,8 @@
#pragma once
class TestUpdateScript
{
public:
void testV2Script();
};

68
src/tests/TestUtils.h Normal file
View 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);

View 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>