Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next
This commit is contained in:
commit
2d7ad25031
@ -250,10 +250,16 @@ namespace canvas
|
||||
|
||||
if( !texture )
|
||||
{
|
||||
// It shouldn't be necessary to allocate an image for the
|
||||
// texture that is the target of dynamic rendering, but
|
||||
// otherwise OSG won't construct all the mipmaps for the texture
|
||||
// and dynamic mipmap generation doesn't work.
|
||||
osg::Image* image = new osg::Image;
|
||||
image->allocateImage(_size_x, _size_y, 1, GL_RGBA, GL_UNSIGNED_BYTE);
|
||||
texture = new osg::Texture2D;
|
||||
texture->setResizeNonPowerOfTwoHint(false);
|
||||
texture->setTextureSize(_size_x, _size_y);
|
||||
texture->setInternalFormat(GL_RGBA);
|
||||
texture->setImage(image);
|
||||
texture->setUnRefImageDataAfterApply(true);
|
||||
}
|
||||
|
||||
updateSampling();
|
||||
|
@ -34,9 +34,11 @@
|
||||
|
||||
#include "simgear/debug/logstream.hxx"
|
||||
#include "simgear/misc/strutils.hxx"
|
||||
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/io/HTTPClient.hxx>
|
||||
#include <simgear/io/sg_file.hxx>
|
||||
#include <simgear/io/untar.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
@ -176,7 +178,8 @@ class HTTPDirectory
|
||||
enum Type
|
||||
{
|
||||
FileType,
|
||||
DirectoryType
|
||||
DirectoryType,
|
||||
TarballType
|
||||
};
|
||||
|
||||
ChildInfo(Type ty, const std::string & nameData, const std::string & hashData) :
|
||||
@ -311,57 +314,39 @@ public:
|
||||
|
||||
void updateChildrenBasedOnHash()
|
||||
{
|
||||
//SG_LOG(SG_TERRASYNC, SG_DEBUG, "updated children for:" << relativePath());
|
||||
|
||||
copyInstalledChildren();
|
||||
|
||||
string_list toBeUpdated, orphans,
|
||||
indexNames = indexChildren();
|
||||
ChildInfoList toBeUpdated;
|
||||
|
||||
simgear::Dir d(absolutePath());
|
||||
PathList fsChildren = d.children(0);
|
||||
PathList orphans = d.children(0);
|
||||
|
||||
for (const auto& child : fsChildren) {
|
||||
const auto& fileName = child.file();
|
||||
if ((fileName == ".dirindex") || (fileName == ".hashes")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ChildInfo info(child.isDir() ? ChildInfo::DirectoryType : ChildInfo::FileType,
|
||||
fileName, "");
|
||||
info.path = child;
|
||||
std::string hash = hashForChild(info);
|
||||
|
||||
ChildInfoList::iterator c = findIndexChild(fileName);
|
||||
if (c == children.end()) {
|
||||
orphans.push_back(fileName);
|
||||
} else if (c->hash != hash) {
|
||||
#if 0
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << fileName);
|
||||
// file exists, but hash mismatch, schedule update
|
||||
if (!hash.empty()) {
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists but hash is wrong for:" << fileName);
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "on disk:" << hash << " vs in info:" << c->hash);
|
||||
}
|
||||
#endif
|
||||
toBeUpdated.push_back(fileName);
|
||||
ChildInfoList::const_iterator it;
|
||||
for (it=children.begin(); it != children.end(); ++it) {
|
||||
// Check if the file exists
|
||||
PathList::const_iterator p = std::find_if(fsChildren.begin(), fsChildren.end(), LocalFileMatcher(*it));
|
||||
if (p == fsChildren.end()) {
|
||||
// File or directory does not exist on local disk, so needs to be updated.
|
||||
toBeUpdated.push_back(ChildInfo(*it));
|
||||
} else if (hashForChild(*it) != it->hash) {
|
||||
// File/directory exists, but hash doesn't match.
|
||||
toBeUpdated.push_back(ChildInfo(*it));
|
||||
orphans.erase(std::remove(orphans.begin(), orphans.end(), *p), orphans.end());
|
||||
} else {
|
||||
// file exists and hash is valid. If it's a directory,
|
||||
// perform a recursive check.
|
||||
if (c->type == ChildInfo::DirectoryType) {
|
||||
HTTPDirectory* childDir = childDirectory(fileName);
|
||||
// File/Directory exists and hash is valid.
|
||||
orphans.erase(std::remove(orphans.begin(), orphans.end(), *p), orphans.end());
|
||||
|
||||
if (it->type == ChildInfo::DirectoryType) {
|
||||
// If it's a directory,perform a recursive check.
|
||||
HTTPDirectory* childDir = childDirectory(it->name);
|
||||
childDir->updateChildrenBasedOnHash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove existing file system children from the index list,
|
||||
// so we can detect new children
|
||||
// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Erase-Remove
|
||||
indexNames.erase(std::remove(indexNames.begin(), indexNames.end(), fileName), indexNames.end());
|
||||
} // of real children iteration
|
||||
|
||||
// all remaining names in indexChilden are new children
|
||||
toBeUpdated.insert(toBeUpdated.end(), indexNames.begin(), indexNames.end());
|
||||
|
||||
// We now have a list of entries that need to be updated, and a list
|
||||
// of orphan files that should be removed.
|
||||
removeOrphans(orphans);
|
||||
scheduleUpdates(toBeUpdated);
|
||||
}
|
||||
@ -372,10 +357,12 @@ public:
|
||||
return _repository->getOrCreateDirectory(childPath);
|
||||
}
|
||||
|
||||
void removeOrphans(const string_list& orphans)
|
||||
void removeOrphans(const PathList orphans)
|
||||
{
|
||||
string_list::const_iterator it;
|
||||
PathList::const_iterator it;
|
||||
for (it = orphans.begin(); it != orphans.end(); ++it) {
|
||||
if (it->file() == ".dirindex") continue;
|
||||
if (it->file() == ".hash") continue;
|
||||
removeChild(*it);
|
||||
}
|
||||
}
|
||||
@ -391,21 +378,20 @@ public:
|
||||
return r;
|
||||
}
|
||||
|
||||
void scheduleUpdates(const string_list& names)
|
||||
void scheduleUpdates(const ChildInfoList names)
|
||||
{
|
||||
string_list::const_iterator it;
|
||||
ChildInfoList::const_iterator it;
|
||||
for (it = names.begin(); it != names.end(); ++it) {
|
||||
ChildInfoList::iterator cit = findIndexChild(*it);
|
||||
if (cit == children.end()) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "scheduleUpdate, unknown child:" << *it);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cit->type == ChildInfo::FileType) {
|
||||
_repository->updateFile(this, *it, cit->sizeInBytes);
|
||||
if (it->type == ChildInfo::FileType) {
|
||||
_repository->updateFile(this, it->name, it->sizeInBytes);
|
||||
} else if (it->type == ChildInfo::DirectoryType){
|
||||
HTTPDirectory* childDir = childDirectory(it->name);
|
||||
_repository->updateDir(childDir, it->hash, it->sizeInBytes);
|
||||
} else if (it->type == ChildInfo::TarballType) {
|
||||
// Download a tarball just as a file.
|
||||
_repository->updateFile(this, it->name, it->sizeInBytes);
|
||||
} else {
|
||||
HTTPDirectory* childDir = childDirectory(*it);
|
||||
_repository->updateDir(childDir, cit->hash, cit->sizeInBytes);
|
||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "Coding error! Unknown Child type to schedule update");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -430,12 +416,58 @@ public:
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "updated file but not found in dir:" << _relativePath << " " << file);
|
||||
} else {
|
||||
if (it->hash != hash) {
|
||||
// we don't erase the file on a hash mismatch, becuase if we're syncing during the
|
||||
SG_LOG(SG_TERRASYNC, SG_INFO, "Checksum error for " << absolutePath() << "/" << file << " " << it->hash << " " << hash);
|
||||
// we don't erase the file on a hash mismatch, because if we're syncing during the
|
||||
// middle of a server-side update, the downloaded file may actually become valid.
|
||||
_repository->failedToUpdateChild(_relativePath, HTTPRepository::REPO_ERROR_CHECKSUM);
|
||||
} else {
|
||||
_repository->updatedFileContents(it->path, hash);
|
||||
_repository->totalDownloaded += sz;
|
||||
SGPath p = SGPath(absolutePath(), file);
|
||||
|
||||
if ((p.extension() == "tgz") || (p.extension() == "zip")) {
|
||||
// We require that any compressed files have the same filename as the file or directory
|
||||
// they expand to, so we can remove the old file/directory before extracting the new
|
||||
// data.
|
||||
SGPath removePath = SGPath(p.base());
|
||||
bool pathAvailable = true;
|
||||
if (removePath.exists()) {
|
||||
if (removePath.isDir()) {
|
||||
simgear::Dir pd(removePath);
|
||||
pathAvailable = pd.removeChildren();
|
||||
} else {
|
||||
pathAvailable = removePath.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (pathAvailable) {
|
||||
// If this is a tarball, then extract it.
|
||||
SGBinaryFile f(p);
|
||||
if (! f.open(SG_IO_IN)) SG_LOG(SG_TERRASYNC, SG_ALERT, "Unable to open " << p << " to extract");
|
||||
|
||||
SG_LOG(SG_TERRASYNC, SG_INFO, "Extracting " << absolutePath() << "/" << file << " to " << p.dir());
|
||||
SGPath extractDir = p.dir();
|
||||
ArchiveExtractor ex(extractDir);
|
||||
|
||||
uint8_t* buf = (uint8_t*) alloca(128);
|
||||
while (!f.eof()) {
|
||||
size_t bufSize = f.read((char*) buf, 128);
|
||||
ex.extractBytes(buf, bufSize);
|
||||
}
|
||||
|
||||
ex.flush();
|
||||
if (! ex.isAtEndOfArchive()) {
|
||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "Corrupt tarball " << p);
|
||||
}
|
||||
|
||||
if (ex.hasError()) {
|
||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "Error extracting " << p);
|
||||
}
|
||||
|
||||
} else {
|
||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "Unable to remove old file/directory " << removePath);
|
||||
} // of pathAvailable
|
||||
} // of handling tgz files
|
||||
} // of hash matches
|
||||
} // of found in child list
|
||||
}
|
||||
@ -458,6 +490,16 @@ private:
|
||||
{ return info.name == name; }
|
||||
};
|
||||
|
||||
struct LocalFileMatcher
|
||||
{
|
||||
LocalFileMatcher(const ChildInfo ci) : childInfo(ci) {}
|
||||
ChildInfo childInfo;
|
||||
|
||||
bool operator()(const SGPath path) const {
|
||||
return path.file() == childInfo.name;
|
||||
}
|
||||
};
|
||||
|
||||
ChildInfoList::iterator findIndexChild(const std::string& name)
|
||||
{
|
||||
return std::find_if(children.begin(), children.end(), ChildWithName(name));
|
||||
@ -519,8 +561,8 @@ private:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeData != "f" && typeData != "d" ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: invalid type in line '" << line << "', expected 'd' or 'f', (ignoring line)"
|
||||
if (typeData != "f" && typeData != "d" && typeData != "t" ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: invalid type in line '" << line << "', expected 't', 'd' or 'f', (ignoring line)"
|
||||
<< "\n\tparsing:" << p.utf8Str());
|
||||
continue;
|
||||
}
|
||||
@ -533,7 +575,11 @@ private:
|
||||
continue;
|
||||
}
|
||||
|
||||
children.emplace_back(ChildInfo(typeData == "f" ? ChildInfo::FileType : ChildInfo::DirectoryType, tokens[1], tokens[2]));
|
||||
ChildInfo ci = ChildInfo(ChildInfo::FileType, tokens[1], tokens[2]);
|
||||
if (typeData == "d") ci.type = ChildInfo::DirectoryType;
|
||||
if (typeData == "t") ci.type = ChildInfo::TarballType;
|
||||
|
||||
children.emplace_back(ci);
|
||||
children.back().path = absolutePath() / tokens[1];
|
||||
if (tokens.size() > 3) {
|
||||
children.back().setSize(tokens[3]);
|
||||
@ -543,40 +589,36 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
void removeChild(const std::string& name)
|
||||
void removeChild(SGPath path)
|
||||
{
|
||||
SGPath p(absolutePath());
|
||||
p.append(name);
|
||||
bool ok;
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "Removing:" << path);
|
||||
|
||||
std::string fpath = _relativePath + "/" + name;
|
||||
if (p.isDir()) {
|
||||
ok = _repository->deleteDirectory(fpath, p);
|
||||
std::string fpath = _relativePath + "/" + path.file();
|
||||
if (path.isDir()) {
|
||||
ok = _repository->deleteDirectory(fpath, path);
|
||||
} else {
|
||||
// remove the hash cache entry
|
||||
_repository->updatedFileContents(p, std::string());
|
||||
ok = p.remove();
|
||||
_repository->updatedFileContents(path, std::string());
|
||||
ok = path.remove();
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "removal failed for:" << p);
|
||||
throw sg_io_exception("Failed to remove existing file/dir:", p);
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "removal failed for:" << path);
|
||||
throw sg_io_exception("Failed to remove existing file/dir:", path);
|
||||
}
|
||||
}
|
||||
|
||||
std::string hashForChild(const ChildInfo& child) const
|
||||
{
|
||||
SGPath p(child.path);
|
||||
if (child.type == ChildInfo::DirectoryType) {
|
||||
p.append(".dirindex");
|
||||
}
|
||||
if (child.type == ChildInfo::DirectoryType) p.append(".dirindex");
|
||||
if (child.type == ChildInfo::TarballType) p.concat(".tgz"); // For tarballs the hash is against the tarball file itself
|
||||
return _repository->hashForPath(p);
|
||||
}
|
||||
|
||||
HTTPRepoPrivate* _repository;
|
||||
std::string _relativePath; // in URL and file-system space
|
||||
|
||||
|
||||
};
|
||||
|
||||
HTTPRepository::HTTPRepository(const SGPath& base, HTTP::Client *cl) :
|
||||
|
@ -61,23 +61,23 @@ public:
|
||||
std::map<string, string> headers;
|
||||
protected:
|
||||
|
||||
virtual void onDone()
|
||||
void onDone() override
|
||||
{
|
||||
complete = true;
|
||||
}
|
||||
|
||||
virtual void onFail()
|
||||
void onFail() override
|
||||
{
|
||||
failed = true;
|
||||
}
|
||||
|
||||
virtual void gotBodyData(const char* s, int n)
|
||||
void gotBodyData(const char* s, int n) override
|
||||
{
|
||||
//std::cout << "got body data:'" << string(s, n) << "'" <<std::endl;
|
||||
// std::cout << "got body data:'" << string(s, n) << "'" <<std::endl;
|
||||
bodyData += string(s, n);
|
||||
}
|
||||
|
||||
virtual void responseHeader(const string& header, const string& value)
|
||||
void responseHeader(const string& header, const string& value) override
|
||||
{
|
||||
Request::responseHeader(header, value);
|
||||
headers[header] = value;
|
||||
@ -783,8 +783,15 @@ cout << "testing proxy close" << endl;
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// disabling this test for now, since it seems to have changed depending
|
||||
// on the libCurl version. (Or some other configuration which is currently
|
||||
// not apparent).
|
||||
// old behaviour: Curl sends the second request soon after makeRequest
|
||||
// new behaviour: Curl waits for the first request to complete, before
|
||||
// sending the second request (i.e acts as if HTTP pipelining is disabled)
|
||||
#if 0
|
||||
{
|
||||
cout << "get-during-response-send" << endl;
|
||||
cout << "get-during-response-send\n\n" << endl;
|
||||
cl.clearAllConnections();
|
||||
//test_get_during_send
|
||||
|
||||
@ -804,7 +811,10 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own2(tr2);
|
||||
cl.makeRequest(tr2);
|
||||
|
||||
waitForComplete(&cl, tr2);
|
||||
SG_VERIFY(waitFor(&cl, [tr, tr2]() {
|
||||
return tr->isComplete() && tr2->isComplete();
|
||||
}));
|
||||
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY3));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY3));
|
||||
@ -812,6 +822,7 @@ cout << "testing proxy close" << endl;
|
||||
SG_CHECK_EQUAL(tr2->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY1));
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
cout << "redirect test" << endl;
|
||||
|
@ -198,9 +198,7 @@ private:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Catalog::Catalog(Root *aRoot) :
|
||||
m_root(aRoot),
|
||||
m_status(Delegate::FAIL_UNKNOWN),
|
||||
m_retrievedTime(0)
|
||||
m_root(aRoot)
|
||||
{
|
||||
}
|
||||
|
||||
@ -221,7 +219,7 @@ CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
|
||||
SGPath xml = aPath;
|
||||
xml.append("catalog.xml");
|
||||
if (!xml.exists()) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr props;
|
||||
@ -229,7 +227,7 @@ CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
|
||||
props = new SGPropertyNode;
|
||||
readProperties(xml, props);
|
||||
} catch (sg_exception& ) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool versionCheckOk = checkVersion(aRoot->applicationVersion(), props);
|
||||
@ -241,18 +239,28 @@ CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "creating catalog from:" << aPath);
|
||||
}
|
||||
|
||||
// check for the marker file we write, to mark a catalog as disabled
|
||||
const SGPath disableMarkerFile = aPath / "_disabled_";
|
||||
|
||||
CatalogRef c = new Catalog(aRoot);
|
||||
c->m_installRoot = aPath;
|
||||
|
||||
if (disableMarkerFile.exists()) {
|
||||
c->m_userEnabled = false;
|
||||
}
|
||||
|
||||
c->parseProps(props);
|
||||
c->parseTimestamp();
|
||||
|
||||
if (!c->validatePackages()) {
|
||||
c->changeStatus(Delegate::FAIL_VALIDATION);
|
||||
} else if (versionCheckOk) {
|
||||
} else if (!versionCheckOk) {
|
||||
c->changeStatus(Delegate::FAIL_VERSION);
|
||||
} else if (!c->m_userEnabled) {
|
||||
c->changeStatus(Delegate::USER_DISABLED);
|
||||
} else {
|
||||
// parsed XML ok, mark status as valid
|
||||
c->changeStatus(Delegate::STATUS_SUCCESS);
|
||||
} else {
|
||||
c->changeStatus(Delegate::FAIL_VERSION);
|
||||
}
|
||||
|
||||
return c;
|
||||
@ -592,6 +600,9 @@ Delegate::StatusCode Catalog::status() const
|
||||
|
||||
bool Catalog::isEnabled() const
|
||||
{
|
||||
if (!m_userEnabled)
|
||||
return false;
|
||||
|
||||
switch (m_status) {
|
||||
case Delegate::STATUS_SUCCESS:
|
||||
case Delegate::STATUS_REFRESHED:
|
||||
@ -604,6 +615,36 @@ bool Catalog::isEnabled() const
|
||||
}
|
||||
}
|
||||
|
||||
bool Catalog::isUserEnabled() const
|
||||
{
|
||||
return m_userEnabled;
|
||||
}
|
||||
|
||||
void Catalog::setUserEnabled(bool b)
|
||||
{
|
||||
if (m_userEnabled == b)
|
||||
return;
|
||||
|
||||
m_userEnabled = b;
|
||||
SGPath disableMarkerFile = installRoot() / "_disabled_";
|
||||
if (m_userEnabled) {
|
||||
sg_ofstream of(disableMarkerFile);
|
||||
of << "1\n"; // touch the file
|
||||
} else {
|
||||
bool ok = disableMarkerFile.remove();
|
||||
if (!ok) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Failed to remove catalog-disable marker file:" << disableMarkerFile);
|
||||
}
|
||||
}
|
||||
|
||||
Delegate::StatusCode effectiveStatus = m_status;
|
||||
if ((m_status == Delegate::STATUS_SUCCESS) && !m_userEnabled) {
|
||||
effectiveStatus = Delegate::USER_DISABLED;
|
||||
}
|
||||
|
||||
m_root->catalogRefreshStatus(this, effectiveStatus);
|
||||
}
|
||||
|
||||
void Catalog::processAlternate(SGPropertyNode_ptr alt)
|
||||
{
|
||||
std::string altId;
|
||||
|
@ -150,6 +150,9 @@ public:
|
||||
{
|
||||
return addStatusCallback(boost::bind(mem_func, instance, _1));
|
||||
}
|
||||
|
||||
bool isUserEnabled() const;
|
||||
void setUserEnabled(bool b);
|
||||
private:
|
||||
Catalog(Root* aRoot);
|
||||
|
||||
@ -185,11 +188,12 @@ private:
|
||||
SGPropertyNode_ptr m_props;
|
||||
SGPath m_installRoot;
|
||||
std::string m_url;
|
||||
Delegate::StatusCode m_status;
|
||||
Delegate::StatusCode m_status = Delegate::FAIL_UNKNOWN;
|
||||
HTTP::Request_ptr m_refreshRequest;
|
||||
bool m_userEnabled = true;
|
||||
|
||||
PackageList m_packages;
|
||||
time_t m_retrievedTime;
|
||||
time_t m_retrievedTime = 0;
|
||||
|
||||
typedef std::map<std::string, Package*> PackageWeakMap;
|
||||
PackageWeakMap m_variantDict;
|
||||
|
@ -15,9 +15,7 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
@ -98,6 +96,11 @@ public:
|
||||
path = "/catalogTest1/movies-data.zip";
|
||||
}
|
||||
|
||||
if (path == "/catalogTest1/b747.tar.gz") {
|
||||
sendErrorResponse(403, false, "Bad URL");
|
||||
return;
|
||||
}
|
||||
|
||||
localPath.append(path);
|
||||
|
||||
// SG_LOG(SG_IO, SG_INFO, "local path is:" << localPath.str());
|
||||
@ -158,7 +161,7 @@ int parseTest()
|
||||
SG_CHECK_EQUAL(cat->description(), "First test catalog");
|
||||
|
||||
// check the packages too
|
||||
SG_CHECK_EQUAL(cat->packages().size(), 5);
|
||||
SG_CHECK_EQUAL(cat->packages().size(), 6);
|
||||
|
||||
pkg::PackageRef p1 = cat->packages().front();
|
||||
SG_CHECK_EQUAL(p1->catalog(), cat.ptr());
|
||||
@ -349,7 +352,7 @@ void testAddCatalog(HTTP::Client* cl)
|
||||
p.append("org.flightgear.test.catalog1");
|
||||
p.append("catalog.xml");
|
||||
SG_VERIFY(p.exists());
|
||||
SG_CHECK_EQUAL(root->allPackages().size(), 5);
|
||||
SG_CHECK_EQUAL(root->allPackages().size(), 6);
|
||||
SG_CHECK_EQUAL(root->catalogs().size(), 1);
|
||||
|
||||
pkg::PackageRef p1 = root->getPackageById("alpha");
|
||||
@ -383,6 +386,7 @@ void testInstallPackage(HTTP::Client* cl)
|
||||
waitForUpdateComplete(cl, root);
|
||||
SG_VERIFY(p1->isInstalled());
|
||||
SG_VERIFY(p1->existingInstall() == ins);
|
||||
SG_CHECK_EQUAL(ins->status(), pkg::Delegate::STATUS_SUCCESS);
|
||||
|
||||
pkg::PackageRef commonDeps = root->getPackageById("common-sounds");
|
||||
SG_VERIFY(commonDeps->existingInstall());
|
||||
@ -428,6 +432,7 @@ void testUninstall(HTTP::Client* cl)
|
||||
|
||||
ins->uninstall();
|
||||
|
||||
SG_CHECK_EQUAL(ins->status(), pkg::Delegate::STATUS_SUCCESS);
|
||||
SG_VERIFY(!ins->path().exists());
|
||||
}
|
||||
|
||||
@ -1075,9 +1080,45 @@ void updateInvalidToInvalid(HTTP::Client* cl)
|
||||
|
||||
}
|
||||
|
||||
void testInstallBadPackage(HTTP::Client* cl)
|
||||
{
|
||||
global_catalogVersion = 0;
|
||||
|
||||
SGPath rootPath(simgear::Dir::current().path());
|
||||
rootPath.append("pkg_install_bad_pkg");
|
||||
simgear::Dir pd(rootPath);
|
||||
pd.removeChildren();
|
||||
|
||||
pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
|
||||
// specify a test dir
|
||||
root->setHTTPClient(cl);
|
||||
|
||||
pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml");
|
||||
waitForUpdateComplete(cl, root);
|
||||
|
||||
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b747-400");
|
||||
pkg::InstallRef ins = p1->install();
|
||||
|
||||
bool didFail = false;
|
||||
ins->fail([&didFail, &ins](pkg::Install* ourInstall) {
|
||||
SG_CHECK_EQUAL(ins, ourInstall);
|
||||
didFail = true;
|
||||
});
|
||||
|
||||
SG_VERIFY(ins->isQueued());
|
||||
|
||||
waitForUpdateComplete(cl, root);
|
||||
SG_VERIFY(!p1->isInstalled());
|
||||
SG_VERIFY(didFail);
|
||||
SG_VERIFY(p1->existingInstall() == ins);
|
||||
SG_CHECK_EQUAL(ins->status(), pkg::Delegate::FAIL_DOWNLOAD);
|
||||
SG_CHECK_EQUAL(ins->path(), rootPath / "org.flightgear.test.catalog1" / "Aircraft" / "b744");
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
sglog().setLogLevels( SG_ALL, SG_DEBUG );
|
||||
// sglog().setLogLevels( SG_ALL, SG_DEBUG );
|
||||
|
||||
HTTP::Client cl;
|
||||
cl.setMaxConnections(1);
|
||||
@ -1116,6 +1157,8 @@ int main(int argc, char* argv[])
|
||||
|
||||
testVersionMigrateToId(&cl);
|
||||
|
||||
testInstallBadPackage(&cl);
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Successfully passed all tests!");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -56,7 +56,8 @@ public:
|
||||
FAIL_HTTP_FORBIDDEN, ///< URL returned a 403. Marked specially to catch rate-limiting
|
||||
FAIL_VALIDATION, ///< catalog or package failed to validate
|
||||
STATUS_REFRESHED,
|
||||
USER_CANCELLED
|
||||
USER_CANCELLED,
|
||||
USER_DISABLED
|
||||
} StatusCode;
|
||||
|
||||
|
||||
|
@ -108,6 +108,8 @@ protected:
|
||||
m_extractor.reset(new ArchiveExtractor(m_extractPath));
|
||||
memset(&m_md5, 0, sizeof(SG_MD5_CTX));
|
||||
SG_MD5Init(&m_md5);
|
||||
|
||||
m_owner->startDownload();
|
||||
}
|
||||
|
||||
virtual void gotBodyData(const char* s, int n)
|
||||
@ -395,6 +397,7 @@ Install* Install::progress(const ProgressCallback& cb)
|
||||
//------------------------------------------------------------------------------
|
||||
void Install::installResult(Delegate::StatusCode aReason)
|
||||
{
|
||||
m_status = aReason;
|
||||
m_package->catalog()->root()->finishInstall(this, aReason);
|
||||
if (aReason == Delegate::STATUS_SUCCESS) {
|
||||
_cb_done(this);
|
||||
@ -412,6 +415,15 @@ void Install::installProgress(unsigned int aBytes, unsigned int aTotal)
|
||||
_cb_progress(this, aBytes, aTotal);
|
||||
}
|
||||
|
||||
void Install::startDownload()
|
||||
{
|
||||
m_status = Delegate::STATUS_IN_PROGRESS;
|
||||
}
|
||||
|
||||
Delegate::StatusCode Install::status() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
} // of namespace pkg
|
||||
|
||||
|
@ -86,6 +86,8 @@ public:
|
||||
|
||||
size_t downloadedBytes() const;
|
||||
|
||||
Delegate::StatusCode status() const;
|
||||
|
||||
/**
|
||||
* full path to the primary -set.xml file for this install
|
||||
*/
|
||||
@ -169,6 +171,7 @@ private:
|
||||
|
||||
void installResult(Delegate::StatusCode aReason);
|
||||
void installProgress(unsigned int aBytes, unsigned int aTotal);
|
||||
void startDownload();
|
||||
|
||||
PackageRef m_package;
|
||||
unsigned int m_revision; ///< revision on disk
|
||||
|
@ -194,6 +194,32 @@
|
||||
<url>http://localhost:2000/catalogTest1/b737.tar.gz</url>
|
||||
</package>
|
||||
|
||||
<package>
|
||||
<id>b747-400</id>
|
||||
<name>Boeing 747-400</name>
|
||||
<dir>b744</dir>
|
||||
<description>A popular four-engined wide-body jet</description>
|
||||
<revision type="int">111</revision>
|
||||
<file-size-bytes type="int">860</file-size-bytes>
|
||||
|
||||
<tag>boeing</tag>
|
||||
<tag>jet</tag>
|
||||
<tag>ifr</tag>
|
||||
|
||||
|
||||
|
||||
<rating>
|
||||
<FDM type="int">5</FDM>
|
||||
<systems type="int">5</systems>
|
||||
<model type="int">4</model>
|
||||
<cockpit type="int">4</cockpit>
|
||||
</rating>
|
||||
|
||||
<md5>a94ca5704f305b90767f40617d194ed6</md5>
|
||||
<!-- this URL will fail, on purpose -->
|
||||
<url>http://localhost:2000/catalogTest1/b747.tar.gz</url>
|
||||
</package>
|
||||
|
||||
<package>
|
||||
<id>common-sounds</id>
|
||||
<name>Common sound files for test catalog aircraft</name>
|
||||
|
Loading…
Reference in New Issue
Block a user