testremotediscovery.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /*
  2. * This software is in the public domain, furnished "as is", without technical
  3. * support, and with no warranty, express or implied, as to its usefulness for
  4. * any purpose.
  5. *
  6. */
  7. #include <QtTest>
  8. #include "syncenginetestutils.h"
  9. #include <syncengine.h>
  10. #include <localdiscoverytracker.h>
  11. using namespace OCC;
  12. struct FakeBrokenXmlPropfindReply : FakePropfindReply {
  13. FakeBrokenXmlPropfindReply(FileInfo &remoteRootFileInfo, QNetworkAccessManager::Operation op,
  14. const QNetworkRequest &request, QObject *parent)
  15. : FakePropfindReply(remoteRootFileInfo, op, request, parent) {
  16. QVERIFY(payload.size() > 50);
  17. // turncate the XML
  18. payload.chop(20);
  19. }
  20. };
  21. struct MissingPermissionsPropfindReply : FakePropfindReply {
  22. MissingPermissionsPropfindReply(FileInfo &remoteRootFileInfo, QNetworkAccessManager::Operation op,
  23. const QNetworkRequest &request, QObject *parent)
  24. : FakePropfindReply(remoteRootFileInfo, op, request, parent) {
  25. // If the propfind contains a single file without permissions, this is a server error
  26. const char toRemove[] = "<oc:permissions>RDNVCKW</oc:permissions>";
  27. auto pos = payload.indexOf(toRemove, payload.size()/2);
  28. QVERIFY(pos > 0);
  29. payload.remove(pos, sizeof(toRemove) - 1);
  30. }
  31. };
  32. enum ErrorKind : int {
  33. // Lower code are corresponding to HTML error code
  34. InvalidXML = 1000,
  35. MissingPermissions,
  36. Timeout,
  37. };
  38. Q_DECLARE_METATYPE(ErrorCategory)
  39. class TestRemoteDiscovery : public QObject
  40. {
  41. Q_OBJECT
  42. private slots:
  43. void testRemoteDiscoveryError_data()
  44. {
  45. qRegisterMetaType<ErrorCategory>();
  46. QTest::addColumn<int>("errorKind");
  47. QTest::addColumn<QString>("expectedErrorString");
  48. QString httpErrorMessage = "Server replied with an error while reading directory 'B' : Internal Server Fake Error";
  49. QTest::newRow("404") << 404 << httpErrorMessage;
  50. QTest::newRow("500") << 500 << httpErrorMessage;
  51. QTest::newRow("503") << 503 << httpErrorMessage;
  52. // 200 should be an error since propfind should return 207
  53. QTest::newRow("200") << 200 << httpErrorMessage;
  54. QTest::newRow("InvalidXML") << +InvalidXML << "error while reading directory 'B' : Unknown error";
  55. QTest::newRow("MissingPermissions") << +MissingPermissions << "error while reading directory 'B' : The server file discovery reply is missing data.";
  56. QTest::newRow("Timeout") << +Timeout << "error while reading directory 'B' : Operation canceled";
  57. }
  58. // Check what happens when there is an error.
  59. void testRemoteDiscoveryError()
  60. {
  61. QFETCH(int, errorKind);
  62. QFETCH(QString, expectedErrorString);
  63. bool syncSucceeds = errorKind == 503; // 503 just ignore the temporarily unavailable directory
  64. FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
  65. // Do Some change as well
  66. fakeFolder.localModifier().insert("A/z1");
  67. fakeFolder.localModifier().insert("B/z1");
  68. fakeFolder.localModifier().insert("C/z1");
  69. fakeFolder.remoteModifier().insert("A/z2");
  70. fakeFolder.remoteModifier().insert("B/z2");
  71. fakeFolder.remoteModifier().insert("C/z2");
  72. auto oldLocalState = fakeFolder.currentLocalState();
  73. auto oldRemoteState = fakeFolder.currentRemoteState();
  74. fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *)
  75. -> QNetworkReply *{
  76. if (req.attribute(QNetworkRequest::CustomVerbAttribute) == "PROPFIND" && req.url().path().endsWith("/B")) {
  77. if (errorKind == InvalidXML) {
  78. return new FakeBrokenXmlPropfindReply(fakeFolder.remoteModifier(), op, req, this);
  79. } else if (errorKind == MissingPermissions) {
  80. return new MissingPermissionsPropfindReply(fakeFolder.remoteModifier(), op, req, this);
  81. } else if (errorKind == Timeout) {
  82. return new FakeHangingReply(op, req, this);
  83. } else if (errorKind < 1000) {
  84. return new FakeErrorReply(op, req, this, errorKind);
  85. }
  86. }
  87. return nullptr;
  88. });
  89. // So the test that test timeout finishes fast
  90. QScopedValueRollback<int> setHttpTimeout(AbstractNetworkJob::httpTimeout, errorKind == Timeout ? 1 : 10000);
  91. QSignalSpy errorSpy(&fakeFolder.syncEngine(), &SyncEngine::syncError);
  92. QCOMPARE(fakeFolder.syncOnce(), syncSucceeds);
  93. qDebug() << "errorSpy=" << errorSpy;
  94. // The folder B should not have been sync'ed (and in particular not removed)
  95. QCOMPARE(oldLocalState.children["B"], fakeFolder.currentLocalState().children["B"]);
  96. QCOMPARE(oldRemoteState.children["B"], fakeFolder.currentRemoteState().children["B"]);
  97. if (!syncSucceeds) {
  98. // Check we got the right error
  99. QCOMPARE(errorSpy.count(), 1);
  100. QVERIFY(errorSpy[0][0].toString().contains(expectedErrorString));
  101. } else {
  102. // The other folder should have been sync'ed as the sync just ignored the faulty dir
  103. QCOMPARE(fakeFolder.currentRemoteState().children["A"], fakeFolder.currentLocalState().children["A"]);
  104. QCOMPARE(fakeFolder.currentRemoteState().children["C"], fakeFolder.currentLocalState().children["C"]);
  105. }
  106. }
  107. };
  108. QTEST_GUILESS_MAIN(TestRemoteDiscovery)
  109. #include "testremotediscovery.moc"