/*========================================================================= medInria Copyright (c) INRIA 2013 - 2018. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =========================================================================*/ #include #include #include #include #include #include #include #include #include #include #include medDataManagerTestObject::medDataManagerTestObject(void) { m_storagePath = QCoreApplication::applicationDirPath() + "/testSqlTempDb"; m_currentId = 0; connect(this, SIGNAL(dataAdded()), &m_eventLoop, SLOT(quit())); } medDataManagerTestObject::~medDataManagerTestObject(void) { } void medDataManagerTestObject::initTestCase(void) { qRegisterMetaType(); QVERIFY( medQtDataImage::registered() ); QVERIFY( medQtDataImageReader::registered() ); QVERIFY( medQtDataImageWriter::registered() ); medStorage::rmpath(m_storagePath); medStorage::setDataLocation( m_storagePath ); QVERIFY( medLocalDbController::instance()->createConnection() ); const int persistentSourceId = 1; const int nonPersistentSourceId = 2; db = medDataManager::instance()->controllerForDataSource(persistentSourceId); QVERIFY( db ); npDb = medDataManager::instance()->controllerForDataSource(nonPersistentSourceId); QVERIFY( npDb ); connect(medDataManager::instance(), SIGNAL(dataAdded(const medDataIndex&)), this, SLOT(onDataAdded(const medDataIndex&))); connect(medDataManager::instance(), SIGNAL(dataRemoved(medDataIndex)), this, SLOT(onDataRemoved(const medDataIndex&))); } void medDataManagerTestObject::init(void) { } void medDataManagerTestObject::cleanup(void) { } void medDataManagerTestObject::cleanupTestCase(void) { removeDir(m_storagePath); } dtkSmartPointer medDataManagerTestObject::createTestData(void) { // Create a data. medAbstractDataFactory *dataFactory = medAbstractDataFactory::instance(); dtkSmartPointer testData = dataFactory->createSmartPointer(medQtDataImage::staticDescription()); QString sDatetime = QDateTime::currentDateTime().toString(); medMetaDataKeys::PatientName.set(testData,"TestPatient" + QString::number(m_currentId)); medMetaDataKeys::StudyDescription.set(testData,"TestStudy" + QString::number(m_currentId)); medMetaDataKeys::SeriesDescription.set(testData,"TestSeries" + QString::number(m_currentId)); medMetaDataKeys::BirthDate.set(testData, sDatetime); m_currentId++; QImage testImage(QSize( 800, 500 ), QImage::Format_Mono ); QPainter painter(&testImage); painter.setRenderHints(QPainter::Antialiasing); painter.setPen(Qt::gray); painter.fillRect(testImage.rect(), Qt::black); painter.drawEllipse(QPoint(400,250), 300, 100); testData->setData( &testImage ); return testData; } void medDataManagerTestObject::testImport(void) { m_currentData = createTestData(); IndexList prevPatients = db->patients(); IndexList prevNPPatients = npDb->patients(); medDataManager::instance()->import(m_currentData); waitForInsertions(); QVERIFY(m_lastInsertedIndex.isValid()); IndexList patients = db->patients(); QCOMPARE(patients.size(), prevPatients.size()+1); IndexList studies = db->studies(m_lastInsertedIndex); QCOMPARE(studies.size(), 1); IndexList series = db->series(studies[0]); QCOMPARE(series.size(), 1); QCOMPARE( npDb->patients().size(), prevNPPatients.size() ); const medDataIndex importedIndex = series[0]; QVERIFY(importedIndex.isValid()); // Check data in db matches original. compareData(); } void medDataManagerTestObject::testImportNonPersistent(void) { m_currentData = createTestData(); IndexList prevNPPatients = npDb->patients(); medDataManager::instance()->importNonPersistent(m_currentData); waitForInsertions(); QVERIFY(m_lastInsertedIndex.isValid()); IndexList patients = npDb->patients(); QCOMPARE(patients.size(), prevNPPatients.size()+1); IndexList studies = npDb->studies(m_lastInsertedIndex); QCOMPARE(studies.size(), 1); IndexList series = npDb->series(m_lastInsertedIndex); QCOMPARE(series.size(), 1); const medDataIndex importedIndex = series[0]; QVERIFY(importedIndex.isValid()); compareData(); } void medDataManagerTestObject::testImport2NonPersistentSeries(void) { // first, import a serie testImportNonPersistent(); IndexList prevNPPatients = npDb->patients(); //and import a second serie, copied from the first one, just changing series description //going back by one to dupplicate the data m_currentId--; m_currentData = createTestData(); medMetaDataKeys::SeriesDescription.set(m_currentData,"TestSeries" + QString::number(m_currentId-1) + "bis"); medDataManager::instance()->importNonPersistent(m_currentData); waitForInsertions(); IndexList patients = npDb->patients(); //patients size should be the same QCOMPARE(patients.size(), prevNPPatients.size()); IndexList studies = npDb->studies(m_lastInsertedIndex); //studies size should be the same QCOMPARE(studies.size(), 1); IndexList series = npDb->series(studies[0]); //but there should be 2 series QCOMPARE(series.size(), 2); compareData(); } void medDataManagerTestObject::testImportFile(void) { //create a file to be imported m_currentData = createTestData(); medQtDataImageWriter writer; writer.setData(m_currentData); m_fileToImport = m_storagePath + "/fileToImport.png"; QVERIFY( writer.write(m_fileToImport) == true ); IndexList prevPatients = db->patients(); medDataManager::instance()->import(m_fileToImport, false); waitForInsertions(); QVERIFY(m_lastInsertedIndex.isValid()); IndexList patients = db->patients(); QCOMPARE(patients.size(), prevPatients.size()+1); IndexList studies = db->studies(m_lastInsertedIndex); QCOMPARE(studies.size(), 1); IndexList series = db->series(studies[0]); QCOMPARE(series.size(), 1); const medDataIndex importedIndex = series[0]; QVERIFY(importedIndex.isValid()); // Check data in db matches original. compareData(); dtkSmartPointer insertedData = medDataManager::instance()->data( m_lastInsertedIndex ); QString patientID = medMetaDataKeys::PatientID.getFirstValue(insertedData); QString serieID = medMetaDataKeys::SeriesID.getFirstValue(insertedData); QFileInfo fileInfo(m_storagePath + "/" + patientID + "/" + serieID ); //check that image file is created QDir dir(m_storagePath + "/" + patientID); QFileInfoList fileInfoList = dir.entryInfoList( QDir::Files ); QStringList fileList; foreach(QFileInfo file, fileInfoList) fileList.append(file.fileName()); //filename is no exactly serie's ID QStringList filter = fileList.filter(fileInfo.fileName()); QVERIFY( filter.size() == 1 ); } void medDataManagerTestObject::testIndexFile(void) { //create a file to be indexed m_currentData = createTestData(); medQtDataImageWriter writer; writer.setData(m_currentData); m_fileToImport = m_storagePath + "/fileToIndex.png"; QVERIFY( writer.write(m_fileToImport) == true ); IndexList prevPatients = db->patients(); medDataManager::instance()->import(m_fileToImport, true); waitForInsertions(); QVERIFY(m_lastInsertedIndex.isValid()); IndexList patients = db->patients(); QCOMPARE(patients.size(), prevPatients.size()+1); IndexList studies = db->studies(m_lastInsertedIndex); QCOMPARE(studies.size(), 1); IndexList series = db->series(studies[0]); QCOMPARE(series.size(), 1); const medDataIndex importedIndex = series[0]; QVERIFY(importedIndex.isValid()); // Check data in db matches original. compareData(); dtkSmartPointer insertedData = medDataManager::instance()->data( m_lastInsertedIndex ); QString patientID = medMetaDataKeys::PatientID.getFirstValue(insertedData); QString serieID = medMetaDataKeys::SeriesID.getFirstValue(insertedData); QDir dir(m_storagePath + "/" + patientID); QFileInfoList fileInfoList = dir.entryInfoList( QDir::Files ); //check that there is no image file created QCOMPARE(fileInfoList.size(), 0); } void medDataManagerTestObject::testImportNonPersistentFile(void) { //create a file to be indexed m_currentData = createTestData(); medQtDataImageWriter writer; writer.setData(m_currentData); m_fileToImport = m_storagePath + "/fileToImportInNPDb.png"; QVERIFY( writer.write(m_fileToImport) == true ); IndexList prevPatients = npDb->patients(); medDataManager::instance()->importNonPersistent(m_fileToImport); waitForInsertions(); QVERIFY(m_lastInsertedIndex.isValid()); IndexList patients = npDb->patients(); QCOMPARE(patients.size(), prevPatients.size()+1); IndexList studies = npDb->studies(m_lastInsertedIndex); QCOMPARE(studies.size(), 1); IndexList series = npDb->series(studies[0]); QCOMPARE(series.size(), 1); const medDataIndex importedIndex = series[0]; QVERIFY(importedIndex.isValid()); // Check data in db matches original. compareData(); } void medDataManagerTestObject::testClearNonPersistentData(void) { IndexList prevNPPatients = npDb->patients(); if( prevNPPatients.size() == 0) { testImportNonPersistent(); testImportNonPersistentFile(); } prevNPPatients = npDb->patients(); QVERIFY( prevNPPatients.size() > 0 ); medDataManager::instance()->clearNonPersistentData(); IndexList patients = npDb->patients(); QCOMPARE( patients.size(), 0 ); QCOMPARE( medDataManager::instance()->nonPersistentDataCount(), 0 ); //TODO: to complete ? } void medDataManagerTestObject::testStoreNonPersistentDataToDatabase (void) { IndexList prevNPPatients = npDb->patients(); if( prevNPPatients.size() == 0 ) { testImportNonPersistent(); testImportNonPersistentFile(); } IndexList prevPatients = db->patients(); prevNPPatients = npDb->patients(); QVERIFY( prevNPPatients.size() > 0 ); medDataManager::instance()->storeNonPersistentDataToDatabase(); waitForInsertions(prevNPPatients.size()); IndexList currentPatients = db->patients(); IndexList currentNPPatients = npDb->patients(); QCOMPARE( currentPatients.size(), prevPatients.size() + prevNPPatients.size()); QCOMPARE( currentNPPatients.size(), 0); QCOMPARE( medDataManager::instance()->nonPersistentDataCount(), 0); } void medDataManagerTestObject::testStoreNonPersistentMultipleDataToDatabase(void) { IndexList prevNPPatients = npDb->patients(); medDataIndex indexFor1Serie; medDataIndex indexFor2Series; if( prevNPPatients.size() == 0) { testImportNonPersistent(); indexFor1Serie = m_lastInsertedIndex; testImport2NonPersistentSeries(); indexFor2Series = m_lastInsertedIndex; } IndexList prevPatients = db->patients(); prevNPPatients = npDb->patients(); QVERIFY( prevNPPatients.size() > 1 ); // Test 1 - store just a serie // retrieve indexes for patient, study,serie medDataIndex patient1; patient1.setDataSourceId(indexFor1Serie.dataSourceId()); patient1.setPatientId(indexFor1Serie.patientId()); medDataIndex study1 = npDb->studies(patient1)[0]; medDataIndex serie1 = npDb->series(study1)[0]; QCOMPARE(serie1, indexFor1Serie); dtkSmartPointer originalData = medDataManager::instance()->data( serie1 ); medDataManager::instance()->storeNonPersistentMultipleDataToDatabase(serie1); waitForInsertions(); QVERIFY( m_lastInsertedIndex.isValidForSeries()); dtkSmartPointer insertedData = medDataManager::instance()->data( m_lastInsertedIndex ); compareData(originalData, insertedData); // Test 2 - store a patient and its associated series // retrieve indexes for patient, study, serie medDataIndex patient2; patient2.setDataSourceId(indexFor2Series.dataSourceId()); patient2.setPatientId(indexFor2Series.patientId()); medDataIndex study2 = npDb->studies(patient2)[0]; IndexList series2 = npDb->series(study2); QVERIFY( series2.size()>1 ); medDataIndex serie2_1 = npDb->series(study2)[0]; medDataIndex serie2_2 = npDb->series(study2)[1]; dtkSmartPointer originalDataSerie2_1 = medDataManager::instance()->data( serie2_1 ); dtkSmartPointer originalDataSerie2_2 = medDataManager::instance()->data( serie2_2 ); medDataManager::instance()->storeNonPersistentMultipleDataToDatabase(patient2); waitForInsertions(series2.size()); QVERIFY( m_insertedIndexes.size() > 1); dtkSmartPointer insertedDataSerie2_1 = medDataManager::instance()->data( m_insertedIndexes[0] ); dtkSmartPointer insertedDataSerie2_2 = medDataManager::instance()->data( m_insertedIndexes[1] ); // we don't know the order of insertion if( medMetaDataKeys::SeriesDescription.getFirstValue(insertedDataSerie2_1) == medMetaDataKeys::SeriesDescription.getFirstValue(originalDataSerie2_1)) { compareData(insertedDataSerie2_1, originalDataSerie2_1); compareData(insertedDataSerie2_2, originalDataSerie2_2); } else if( medMetaDataKeys::SeriesDescription.getFirstValue(insertedDataSerie2_1) == medMetaDataKeys::SeriesDescription.getFirstValue(originalDataSerie2_2)) { compareData(insertedDataSerie2_1, originalDataSerie2_2); compareData(insertedDataSerie2_2, originalDataSerie2_1); } else QVERIFY( false ); } void medDataManagerTestObject::testStoreNonPersistentSingleDataToDatabase(void) { IndexList prevNPPatients = npDb->patients(); medDataIndex indexFor1Serie; medDataIndex indexFor2Series; if( prevNPPatients.size() == 0) { testImportNonPersistent(); indexFor1Serie = m_lastInsertedIndex; testImport2NonPersistentSeries(); indexFor2Series = m_lastInsertedIndex; } IndexList prevPatients = db->patients(); prevNPPatients = npDb->patients(); QVERIFY( prevNPPatients.size() > 1 ); // retrieve indexes for patient, study,serie medDataIndex patient1; patient1.setDataSourceId(indexFor1Serie.dataSourceId()); patient1.setPatientId(indexFor1Serie.patientId()); medDataIndex study1 = npDb->studies(patient1)[0]; medDataIndex serie1 = npDb->series(study1)[0]; QCOMPARE(serie1, indexFor1Serie); dtkSmartPointer originalData = medDataManager::instance()->data( serie1 ); medDataManager::instance()->storeNonPersistentMultipleDataToDatabase(serie1); waitForInsertions(); QVERIFY( m_lastInsertedIndex.isValidForSeries()); dtkSmartPointer insertedData = medDataManager::instance()->data( m_lastInsertedIndex ); compareData(originalData, insertedData); } void medDataManagerTestObject::testRemoveData(void) { foreach(int datasource, medDataManager::instance()->dataSourceIds()) { medAbstractDbController *controller = medDataManager::instance()->controllerForDataSource(datasource); IndexList prevPatients = controller->patients(); //inserting at least 1 patient in the db if( prevPatients.size() == 0 ) { if( controller->isPersistent()) testImport(); else testImportNonPersistent(); } prevPatients = controller->patients(); connect(this, SIGNAL(dataRemoved()), &m_eventLoop, SLOT(quit())); int nbPatients = prevPatients.size(); foreach(medDataIndex patient, prevPatients) { medDataIndex study, serie; IndexList studies = controller->studies(patient); if( studies.size( )> 0 ) { study = controller->studies(patient)[0]; if( controller->series(study).size() > 0 ) { serie = controller->series(study)[0]; } } int nbDeletions = 0; foreach(medDataIndex tmpStudy, studies) { nbDeletions += controller->series(tmpStudy).count(); } nbDeletions += studies.count() + 1; medDataManager::instance()->removeData(patient); if(datasource==1) { //this only work for persisitent controller, since, NPcontroller remove() is synchronous waitForDeletions(nbDeletions); } IndexList patients = controller->patients(); QCOMPARE( patients.size(), nbPatients-1 ); nbPatients--; dtkSmartPointer data = medDataManager::instance()->data( patient ); QVERIFY(!data); data = medDataManager::instance()->data( study ); QVERIFY(!data); data = medDataManager::instance()->data( serie ); QVERIFY(!data); } } // let's check that image files are deleted QDir dir(m_storagePath); QFileInfoList fileInfoList = dir.entryInfoList( QDir::Dirs | QDir::NoDotAndDotDot ); QCOMPARE (fileInfoList.size(), 0); } void medDataManagerTestObject::testMoveStudy(void) { //let's clean up everything testRemoveData(); foreach(int datasource, medDataManager::instance()->dataSourceIds()) { medAbstractDbController *controller = medDataManager::instance()->controllerForDataSource(datasource); IndexList prevPatients = controller->patients(); //inserting at least 2 patients in the db if(prevPatients.size()<2) { for(int i=prevPatients.size(); i<2; i++) { if( controller->isPersistent()) testImport(); else testImportNonPersistent(); } } prevPatients = controller->patients(); IndexList prevPatient1Studies = controller->studies(prevPatients[0]); IndexList prevPatient2Studies = controller->studies(prevPatients[1]); medDataIndex study1 = prevPatient1Studies[0]; IndexList newIndexes = medDataManager::instance()->moveStudy(study1, prevPatients[1]); IndexList patient1Studies = controller->studies(prevPatients[0]); IndexList patient2Studies = controller->studies(prevPatients[1]); QCOMPARE( patient2Studies.size(), prevPatient2Studies.size()+1 ); QCOMPARE( patient1Studies.size(), prevPatient1Studies.size()-1 ); } } void medDataManagerTestObject::testMoveSeries(void) { //let's clean up everything testRemoveData(); IndexList testPatients = db->patients(); IndexList testPatients1 = npDb->patients(); foreach(int datasource, medDataManager::instance()->dataSourceIds()) { medAbstractDbController *controller = medDataManager::instance()->controllerForDataSource(datasource); IndexList prevPatients = controller->patients(); //inserting at least 2 patients in the db if(prevPatients.size()<2) { for(int i=prevPatients.size(); i<2; i++) { if( controller->isPersistent()) testImport(); else testImportNonPersistent(); } } prevPatients = controller->patients(); IndexList prevPatient1Studies = controller->studies(prevPatients[0]); IndexList prevPatient2Studies = controller->studies(prevPatients[1]); medDataIndex study1 = prevPatient1Studies[0]; medDataIndex serie1 = controller->series(study1)[0]; medDataIndex study2 = prevPatient2Studies[0]; IndexList prevStudy1Series = controller->series(study1); IndexList prevStudy2Series = controller->series(study2); dtkSmartPointer originalData = medDataManager::instance()->data( serie1 ); QString originalPatientName = medMetaDataKeys::PatientName.getFirstValue(originalData); medDataIndex newIndex = medDataManager::instance()->moveSeries(serie1, study2); IndexList study1Series = controller->series(study1); IndexList study2Series = controller->series(study2); QCOMPARE( study2Series.size(), prevStudy2Series.size()+1 ); QCOMPARE( study1Series.size(), prevStudy1Series.size()-1 ); QVERIFY (newIndex.isValid()); dtkSmartPointer movedData = medDataManager::instance()->data( newIndex ); QString newPatientName = medMetaDataKeys::PatientName.getFirstValue(movedData); compareData(originalData, movedData); } //test to see if everything is deleted properly testRemoveData(); } void medDataManagerTestObject::onDataAdded(const medDataIndex& index) { if(index.isValidForSeries()) { m_lastInsertedIndex = index; qDebug() << "A serie has been inserted."; emit dataAdded(); } else qDebug() << "patient or study inserted"; } void medDataManagerTestObject::onDataRemoved(const medDataIndex& index) { if(index.isValidForSeries()) { qDebug() << "A serie has been removed."; } else qDebug() << "patient or study removed"; emit dataRemoved(); } bool medDataManagerTestObject::removeDir(const QString & dirName) { bool result = false; QDir dir(dirName); if (dir.exists(dirName)) { Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) { if (info.isDir()) { result = removeDir(info.absoluteFilePath()); } else { result = QFile::remove(info.absoluteFilePath()); } if (!result) { return result; } } result = dir.rmdir(dirName); } return result; } void medDataManagerTestObject::compareData() { dtkSmartPointer insertedData = medDataManager::instance()->data( m_lastInsertedIndex ); QVERIFY(insertedData); QVERIFY(m_currentData); QCOMPARE(insertedData->identifier(), m_currentData->identifier()); QCOMPARE(medMetaDataKeys::PatientName.getFirstValue(insertedData), medMetaDataKeys::PatientName.getFirstValue(m_currentData)); QCOMPARE(medMetaDataKeys::StudyDescription.getFirstValue(insertedData), medMetaDataKeys::StudyDescription.getFirstValue(m_currentData)); QCOMPARE(medMetaDataKeys::SeriesDescription.getFirstValue(insertedData), medMetaDataKeys::SeriesDescription.getFirstValue(m_currentData)); } void medDataManagerTestObject::compareData(dtkSmartPointer data1, dtkSmartPointer data2) { QVERIFY(data1); QVERIFY(data2); QCOMPARE(data1->identifier(), data2->identifier()); QCOMPARE(medMetaDataKeys::PatientName.getFirstValue(data1), medMetaDataKeys::PatientName.getFirstValue(data2)); QCOMPARE(medMetaDataKeys::StudyDescription.getFirstValue(data1), medMetaDataKeys::StudyDescription.getFirstValue(data2)); QCOMPARE(medMetaDataKeys::SeriesDescription.getFirstValue(data1), medMetaDataKeys::SeriesDescription.getFirstValue(data2)); } void medDataManagerTestObject::waitForInsertions(int numberOfInsertions, int timeout ) { m_insertedIndexes.clear(); QSignalSpy spy1 (medDataManager::instance(), SIGNAL(dataAdded(const medDataIndex&))); QTimer timer1, timer2; timer1.setSingleShot(true); timer1.start(timeout); connect(&timer2, SIGNAL(timeout()), &m_eventLoop, SLOT(quit())); while( spy1.count() < numberOfInsertions && timer1.isActive() ) { timer2.start(5000); m_eventLoop.exec(); m_insertedIndexes.append(m_lastInsertedIndex); } //TODO: in some cases it seems that there are more signals, need to check that QVERIFY(spy1.count() >= numberOfInsertions); } void medDataManagerTestObject::waitForDeletions(int numberOfDeletions, int timeout ) { m_insertedIndexes.clear(); QSignalSpy spy1 (medDataManager::instance(), SIGNAL(dataRemoved(const medDataIndex&))); QTimer timer1, timer2; timer1.setSingleShot(true); timer1.start(timeout); connect(&timer2, SIGNAL(timeout()), &m_eventLoop, SLOT(quit())); while( spy1.count() < numberOfDeletions && timer1.isActive() ) { timer2.start(5000); m_eventLoop.exec(); } QVERIFY(spy1.count() >= numberOfDeletions); } DTKTEST_MAIN(medDataManagerTest,medDataManagerTestObject)