1 /*=========================================================================
2 
3   Library:   CTK
4 
5   Copyright (c) 2013 University College London, Centre for Medical Image Computing
6 
7   Licensed under the Apache License, Version 2.0 (the "License");
8   you may not use this file except in compliance with the License.
9   You may obtain a copy of the License at
10 
11       http://www.apache.org/licenses/LICENSE-2.0.txt
12 
13   Unless required by applicable law or agreed to in writing, software
14   distributed under the License is distributed on an "AS IS" BASIS,
15   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   See the License for the specific language governing permissions and
17   limitations under the License.
18 
19 =========================================================================*/
20 
21 #include "ctkXnatSessionTest.h"
22 
23 #include <QCoreApplication>
24 #include <QCryptographicHash>
25 #include <QDebug>
26 #include <QDir>
27 #include <QSignalSpy>
28 #include <QStringList>
29 #include <QTest>
30 #include <QTime>
31 #include <QTimer>
32 #include <QUrl>
33 #include <QUuid>
34 
35 #include <ctkXnatDataModel.h>
36 #include <ctkXnatException.h>
37 #include <ctkXnatFile.h>
38 #include <ctkXnatLoginProfile.h>
39 #include <ctkXnatProject.h>
40 #include <ctkXnatResource.h>
41 #include <ctkXnatResourceFolder.h>
42 #include <ctkXnatSession.h>
43 #include <ctkXnatSubject.h>
44 
45 class ctkXnatSessionTestCasePrivate
46 {
47 public:
48   ctkXnatSession* Session;
49 
50   ctkXnatLoginProfile LoginProfile;
51 
52   QString Project;
53   QString Subject;
54   QString Experiment;
55 
56   QDateTime DateTime;
57 };
58 
59 // --------------------------------------------------------------------------
ctkXnatSessionTestCase()60 ctkXnatSessionTestCase::ctkXnatSessionTestCase()
61 : d_ptr(new ctkXnatSessionTestCasePrivate())
62 {
63 }
64 
65 // --------------------------------------------------------------------------
~ctkXnatSessionTestCase()66 ctkXnatSessionTestCase::~ctkXnatSessionTestCase()
67 {
68 }
69 
70 // --------------------------------------------------------------------------
initTestCase()71 void ctkXnatSessionTestCase::initTestCase()
72 {
73   Q_D(ctkXnatSessionTestCase);
74 
75   d->LoginProfile.setName("ctk");
76   d->LoginProfile.setServerUrl(QString("https://central.xnat.org"));
77   d->LoginProfile.setUserName("ctk");
78   d->LoginProfile.setPassword("ctk-xnat2015");
79 }
80 
81 // --------------------------------------------------------------------------
init()82 void ctkXnatSessionTestCase::init()
83 {
84   Q_D(ctkXnatSessionTestCase);
85 
86   d->DateTime = QDateTime::currentDateTime();
87   d->Session = new ctkXnatSession(d->LoginProfile);
88   d->Session->open();
89 }
90 
91 // --------------------------------------------------------------------------
cleanupTestCase()92 void ctkXnatSessionTestCase::cleanupTestCase()
93 {
94 }
95 
96 // --------------------------------------------------------------------------
cleanup()97 void ctkXnatSessionTestCase::cleanup()
98 {
99   Q_D(ctkXnatSessionTestCase);
100 
101   delete d->Session;
102   d->Session = NULL;
103 }
104 
105 // --------------------------------------------------------------------------
testProjectList()106 void ctkXnatSessionTestCase::testProjectList()
107 {
108   Q_D(ctkXnatSessionTestCase);
109 
110   ctkXnatObject* dataModel = d->Session->dataModel();
111   dataModel->fetch();
112 
113   QList<ctkXnatObject*> projects = dataModel->children();
114 
115   QVERIFY(projects.size() > 0);
116 }
117 
118 // --------------------------------------------------------------------------
testResourceUri()119 void ctkXnatSessionTestCase::testResourceUri()
120 {
121   Q_D(ctkXnatSessionTestCase);
122 
123   ctkXnatObject* dataModel = d->Session->dataModel();
124   QVERIFY(!dataModel->resourceUri().isNull());
125   QVERIFY(dataModel->resourceUri().isEmpty());
126 }
127 
128 // --------------------------------------------------------------------------
testParentChild()129 void ctkXnatSessionTestCase::testParentChild()
130 {
131   Q_D(ctkXnatSessionTestCase);
132 
133   ctkXnatDataModel* dataModel = d->Session->dataModel();
134 
135   ctkXnatProject* project = new ctkXnatProject(dataModel);
136 
137   QVERIFY(project->parent() == dataModel);
138 
139   QVERIFY(dataModel->children().contains(project));
140 
141   dataModel->add(project);
142 
143   int numberOfOccurrences = 0;
144   foreach (ctkXnatObject* serverProject, dataModel->children())
145   {
146     if (serverProject == project || serverProject->id() == project->id())
147     {
148       ++numberOfOccurrences;
149     }
150   }
151   QVERIFY(numberOfOccurrences == 1);
152 
153   dataModel->remove(project);
154   numberOfOccurrences = 0;
155   foreach (ctkXnatObject* serverProject, dataModel->children())
156   {
157     if (serverProject == project || serverProject->id() == project->id())
158     {
159       ++numberOfOccurrences;
160     }
161   }
162   QVERIFY(numberOfOccurrences == 0);
163   delete project;
164 }
165 
166 // --------------------------------------------------------------------------
testSession()167 void ctkXnatSessionTestCase::testSession()
168 {
169   Q_D(ctkXnatSessionTestCase);
170 
171   QVERIFY(d->Session->isOpen());
172   QDateTime expirationDate = d->Session->expirationDate();
173 
174   QVERIFY(d->DateTime < expirationDate);
175 
176   QTest::qSleep(2000);
177 
178   QUuid uuid = d->Session->httpGet("/data/version");
179   QVERIFY(!uuid.isNull());
180   d->Session->httpSync(uuid);
181 
182   QVERIFY(expirationDate < d->Session->expirationDate());
183 
184   try
185   {
186     d->Session->httpSync(uuid);
187     QFAIL("Exception for unknown uuid expected");
188   }
189   catch(const ctkInvalidArgumentException&)
190   {}
191 
192   d->Session->close();
193   try
194   {
195     d->Session->dataModel();
196     QFAIL("Exception for closed session expected");
197   }
198   catch(const ctkXnatInvalidSessionException&)
199   {}
200 }
201 
202 // --------------------------------------------------------------------------
testAuthenticationError()203 void ctkXnatSessionTestCase::testAuthenticationError()
204 {
205   ctkXnatLoginProfile loginProfile;
206   loginProfile.setName("error");
207   loginProfile.setServerUrl(QString("https://central.xnat.org"));
208   loginProfile.setUserName("x");
209   loginProfile.setPassword("y");
210 
211   ctkXnatSession session(loginProfile);
212   try
213   {
214     session.open();
215     QFAIL("Authenication error exception expected");
216   }
217   catch (const ctkXnatAuthenticationException&)
218   {}
219 }
220 
221 // --------------------------------------------------------------------------
testCreateProject()222 void ctkXnatSessionTestCase::testCreateProject()
223 {
224   Q_D(ctkXnatSessionTestCase);
225 
226   ctkXnatDataModel* dataModel = d->Session->dataModel();
227 
228   QString projectId = QString("CTK_") + QUuid::createUuid().toString().mid(1, 8);
229   d->Project = projectId;
230 
231   ctkXnatProject* project = new ctkXnatProject(dataModel);
232   project->setId(projectId);
233   project->setName(projectId);
234   project->setDescription("CTK_test_project");
235 
236   bool exists = d->Session->exists(project);
237   QVERIFY(!exists);
238 
239   project->save();
240 
241   exists = d->Session->exists(project);
242   QVERIFY(exists);
243 
244   d->Session->remove(project);
245 
246   exists = d->Session->exists(project);
247   QVERIFY(!exists);
248 }
249 
250 // --------------------------------------------------------------------------
testCreateSubject()251 void ctkXnatSessionTestCase::testCreateSubject()
252 {
253   Q_D(ctkXnatSessionTestCase);
254 
255   ctkXnatDataModel* dataModel = d->Session->dataModel();
256 
257   QString projectId = QString("CTK_") + QUuid::createUuid().toString().mid(1, 8);
258   d->Project = projectId;
259 
260   ctkXnatProject* project = new ctkXnatProject(dataModel);
261   project->setId(projectId);
262   project->setName(projectId);
263   project->setDescription("CTK_test_project");
264 
265   QVERIFY(!project->exists());
266 
267   project->save();
268 
269   QVERIFY(project->exists());
270 
271   ctkXnatSubject* subject = new ctkXnatSubject(project);
272 
273   QString subjectName = QString("CTK_S") + QUuid::createUuid().toString().mid(1, 8);
274   subject->setName(subjectName);
275 
276   subject->save();
277 
278   QVERIFY(!subject->id().isNull());
279 
280   subject->erase();
281 
282   QVERIFY(!subject->exists());
283 
284   project->erase();
285 
286   QVERIFY(!project->exists());
287 }
288 
289 // --------------------------------------------------------------------------
testAddResourceFolder()290 void ctkXnatSessionTestCase::testAddResourceFolder()
291 {
292   Q_D(ctkXnatSessionTestCase);
293 
294   ctkXnatDataModel* dataModel = d->Session->dataModel();
295 
296   QString projectId = QString("CTK_") + QUuid::createUuid().toString().mid(1, 8);
297   d->Project = projectId;
298 
299   ctkXnatProject* project = new ctkXnatProject(dataModel);
300   project->setId(projectId);
301   project->setName(projectId);
302   project->setDescription("CTK_test_project");
303 
304   QVERIFY(!project->exists());
305 
306   project->save();
307 
308   QVERIFY(project->exists());
309 
310   ctkXnatResource* resource = project->addResourceFolder("TestResource", "testFormat", "testContent", "testTag1,testTag2");
311   QVERIFY(resource->exists());
312   QVERIFY(resource->name() == "TestResource");
313   QVERIFY(resource->format() == "testFormat");
314   QVERIFY(resource->content() == "testContent");
315   QVERIFY(resource->tags() == "testTag1,testTag2");
316 
317   ctkXnatResourceFolder* folder = dynamic_cast<ctkXnatResourceFolder*>(resource->parent());
318   QVERIFY(folder->exists());
319   QVERIFY(folder != 0);
320   QVERIFY(folder->name() == "Resources");
321 
322   project->erase();
323   QVERIFY(!project->exists());
324   QVERIFY(!folder->exists());
325   QVERIFY(!resource->exists());
326 }
327 
328 // --------------------------------------------------------------------------
testUploadAndDownloadFile()329 void ctkXnatSessionTestCase::testUploadAndDownloadFile()
330 {
331   Q_D(ctkXnatSessionTestCase);
332 
333   ctkXnatDataModel* dataModel = d->Session->dataModel();
334 
335   QString projectId = QString("CTK_") + QUuid::createUuid().toString().mid(1, 8);
336   d->Project = projectId;
337 
338   ctkXnatProject* project = new ctkXnatProject(dataModel);
339   project->setId(projectId);
340   project->setName(projectId);
341   project->setDescription("CTK_test_project");
342 
343   QVERIFY(!project->exists());
344 
345   project->save();
346 
347   QVERIFY(project->exists());
348 
349   ctkXnatResource* resource = project->addResourceFolder("TestResourceContainingData");
350   QVERIFY(resource->exists());
351   QVERIFY(resource->name() == "TestResourceContainingData");
352   QVERIFY(resource->format() == "");
353   QVERIFY(resource->content() == "");
354   QVERIFY(resource->tags() == "");
355 
356   QString tempDirPath = QDir::tempPath() + QUuid::createUuid().toString().mid(1, 8);
357   QString uploadFileName = tempDirPath + "/ctk_xnat_upload_" + QUuid::createUuid().toString().mid(1, 8) + ".txt";
358   QString downloadFileName = tempDirPath + "/ctk_xnat_download_" + QUuid::createUuid().toString().mid(1, 8) + ".txt";
359 
360   QDir tempDir;
361   if (tempDir.mkdir(tempDirPath))
362   {
363     QFile uploadedFile(uploadFileName);
364 
365     if (uploadedFile.open(QFile::ReadWrite))
366     {
367       QTextStream stream( &uploadedFile );
368       stream << "Hi, I am a CTK test file! ;-)" << endl;
369 
370       QFileInfo fileInfo;
371       fileInfo.setFile(uploadFileName);
372       // Create xnatFile object
373       ctkXnatFile* xnatFile = new ctkXnatFile(resource);
374       xnatFile->setLocalFilePath(fileInfo.filePath());
375       xnatFile->setName(fileInfo.fileName());
376       xnatFile->setFileFormat("some format");
377       xnatFile->setFileContent("some content");
378       xnatFile->setFileTags("some, tags");
379       resource->add(xnatFile);
380 
381       // Actual file upload
382       xnatFile->save();
383 
384       QVERIFY(xnatFile->exists());
385 
386       xnatFile->download(downloadFileName);
387 
388       QFile downloadedFile(downloadFileName);
389 
390       QVERIFY(downloadedFile.exists());
391 
392       uploadedFile.close();
393       if (downloadedFile.open(QFile::ReadOnly) && uploadedFile.open(QFile::ReadOnly))
394       {
395         QCryptographicHash hashUploaded(QCryptographicHash::Md5);
396         QCryptographicHash hashDownloaded(QCryptographicHash::Md5);
397 
398 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
399         hashUploaded.addData(&uploadedFile);
400         hashDownloaded.addData(&downloadedFile);
401 #else
402         hashUploaded.addData(uploadedFile.readAll());
403         hashDownloaded.addData(downloadedFile.readAll());
404 #endif
405 
406         QString md5ChecksumUploaded(hashUploaded.result().toHex());
407         QString md5ChecksumDownloaded(hashDownloaded.result().toHex());
408 
409         QVERIFY (md5ChecksumDownloaded == md5ChecksumUploaded);
410 
411         // Remove the data from XNAT
412         project->erase();
413         QVERIFY(!project->exists());
414         QVERIFY(!resource->exists());
415         QVERIFY(!xnatFile->exists());
416 
417         // Remove the local data
418         uploadedFile.close();
419         downloadedFile.close();
420         uploadedFile.remove();
421         downloadedFile.remove();
422         tempDir.cdUp();
423         tempDir.rmdir(tempDirPath);
424       }
425       else
426       {
427         qWarning()<<"Could not open files for validation! Could not finish test!";
428       }
429     }
430     else
431     {
432       qWarning()<<"Could not create temporary file for upload! Could not finish test!";
433       return;
434     }
435   }
436   else
437   {
438     qWarning()<<"Could not create temporary directory! Could not finish test!";
439   }
440 }
441 
442 // --------------------------------------------------------------------------
ctkXnatSessionTest(int argc,char * argv[])443 int ctkXnatSessionTest(int argc, char* argv[])
444 {
445   QCoreApplication app(argc, argv);
446   ctkXnatSessionTestCase test;
447   return QTest::qExec(&test, argc, argv);
448 }
449