/
klischa
/
AstraScanner2
Обзор
Документация
Войти
/
klischa
/
AstraScanner2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tests/test_integration.cpp
373 строки
12 KB
klischa
fix: stopCapture bool, M_PI в тестах, writeMetadata в removeScan/renameScan
19 июн 2026, 22:39
19 июн 2026, 22:39
cabab0a
Код
Авторство
О чём код?
#include <gtest/gtest.h> #include <QCoreApplication> #include <QTemporaryDir> #include <QJsonDocument> #include <QJsonObject> #include <QJsonArray> #include <pcl/point_cloud.h> #include <pcl/point_types.h> #include <pcl/io/ply_io.h> #include <pcl/io/pcd_io.h> #include "../src/project/ProjectManager.h" #include "../src/filters/PointCloudFilters.h" #include "../src/export/ExportManager.h" using CloudPtr = pcl::PointCloud<pcl::PointXYZRGB>::Ptr; class IntegrationTest : public ::testing::Test { protected: void SetUp() override { if (!QCoreApplication::instance()) { static int argc = 1; static char arg0[] = "test_integration"; static char* argv[] = { arg0 }; new QCoreApplication(argc, argv); } tempDir.reset(new QTemporaryDir()); ASSERT_TRUE(tempDir->isValid()); projectDir = tempDir->path() + "/integration_test"; } void TearDown() override { tempDir.reset(); } CloudPtr createTestCloud(int numPoints = 100) { CloudPtr cloud(new pcl::PointCloud<pcl::PointXYZRGB>); for (int i = 0; i < numPoints; ++i) { pcl::PointXYZRGB pt; pt.x = 0.05 * (i % 10) * 0.01f; pt.y = 0.05 * (i / 10) * 0.01f; pt.z = 0.005 * (i % 5) * 0.01f; pt.r = 255; pt.g = 255; pt.b = 255; cloud->push_back(pt); } return cloud; } std::unique_ptr<QTemporaryDir> tempDir; QString projectDir; }; // Тест: Полный пайплайн сканирования - проект -> скан -> фильтры -> экспорт TEST_F(IntegrationTest, FullScanPipeline) { // 1. Создаем проект ProjectManager pm; bool result = pm.newProject(projectDir, "Integration Test"); ASSERT_TRUE(result); EXPECT_TRUE(pm.isOpen()); // 2. Добавляем несколько сканов CloudPtr cloud1 = createTestCloud(100); CloudPtr cloud2 = createTestCloud(150); CloudPtr cloud3 = createTestCloud(200); pm.addScan(cloud1, "Scan 1"); pm.addScan(cloud2, "Scan 2"); pm.addScan(cloud3, "Scan 3"); EXPECT_EQ(pm.scanCount(), 3); // 3. Сохраняем проект result = pm.saveProject(); ASSERT_TRUE(result); // 4. Загружаем сканы обратно CloudPtr loaded1 = pm.scanCloud(0); CloudPtr loaded2 = pm.scanCloud(1); CloudPtr loaded3 = pm.scanCloud(2); EXPECT_EQ(loaded1->size(), 100); EXPECT_EQ(loaded2->size(), 150); EXPECT_EQ(loaded3->size(), 200); // 5. Применяем фильтры PointCloudFilters filters; // VoxelGrid CloudPtr filtered1 = filters.applyVoxelGrid(loaded1, 0.01f); EXPECT_LE(filtered1->size(), loaded1->size()); // SOR CloudPtr filtered2 = filters.applyStatisticalOutlierRemoval(loaded2, 50, 1.0); EXPECT_LE(filtered2->size(), loaded2->size()); // 6. Экспорт ExportManager em; QString exportPath = projectDir + "/exported_cloud.ply"; result = em.savePointCloud(filtered1, exportPath, ExportManager::CloudFormat::PLY); ASSERT_TRUE(result); EXPECT_TRUE(QFileInfo::exists(exportPath)); // 7. Загружаем экспорт и проверяем CloudPtr imported(new pcl::PointCloud<pcl::PointXYZRGB>); int rc = pcl::io::loadPLYFile(exportPath.toStdString(), *imported); EXPECT_EQ(rc, 0); EXPECT_GT(imported->size(), 0); // 8. Закрываем проект pm.closeProject(); EXPECT_FALSE(pm.isOpen()); } // Тест: Объединение сканов через ICP TEST_F(IntegrationTest, MergeScansICP) { // Создаем проект ProjectManager pm; bool result = pm.newProject(projectDir, "Merge Test"); ASSERT_TRUE(result); // Добавляем 3 скана с небольшими смещениями std::vector<pcl::PointCloud<pcl::PointXYZRGB>::Ptr> scans; for (int scanIdx = 0; scanIdx < 3; ++scanIdx) { CloudPtr cloud(new pcl::PointCloud<pcl::PointXYZRGB>); for (int i = 0; i < 50; ++i) { pcl::PointXYZRGB pt; pt.x = 0.08 * cos(i * 0.1f) + scanIdx * 0.05; pt.y = 0.08 * sin(i * 0.1f); pt.z = 0.01 * (i % 5); pt.r = 255; pt.g = 255; pt.b = 255; cloud->push_back(pt); } scans.push_back(cloud); pm.addScan(cloud, "Scan " + QString::number(scanIdx + 1)); } // Объединяем сканы PointCloudFilters filters; PointCloudFilters::MergeParams params; params.maxCorrespondenceDistance = 0.1; params.maximumIterations = 50; params.skipNonConverged = false; params.voxelLeafOut = 0.0; CloudPtr merged = filters.mergeScans(scans, params); // Результат должен содержать точки из всех сканов EXPECT_GT(merged->size(), scans[0]->size()); EXPECT_GT(merged->size(), scans[1]->size()); EXPECT_GT(merged->size(), scans[2]->size()); } // Тест: Экспорт меша TEST_F(IntegrationTest, MeshExport) { // Создаем проект ProjectManager pm; bool result = pm.newProject(projectDir, "Mesh Export Test"); ASSERT_TRUE(result); // Добавляем плотное облако CloudPtr cloud(new pcl::PointCloud<pcl::PointXYZRGB>); for (int i = 0; i < 1000; ++i) { double r = 0.05 * sqrt(static_cast<double>(i) / 1000); double theta = 2 * 3.14159265358979323846 * i / 1000; pcl::PointXYZRGB pt; pt.x = r * cos(theta); pt.y = r * sin(theta); pt.z = 0.001 * (i % 3 - 1); pt.r = 255; pt.g = 255; pt.b = 255; cloud->push_back(pt); } pm.addScan(cloud, "Disk Cloud"); // Экспорт PLY ExportManager em; QString exportPath = projectDir + "/disk_cloud.ply"; result = em.savePointCloud(cloud, exportPath, ExportManager::CloudFormat::PLY); ASSERT_TRUE(result); // Загружаем обратно CloudPtr imported(new pcl::PointCloud<pcl::PointXYZRGB>); int rc = pcl::io::loadPLYFile(exportPath.toStdString(), *imported); EXPECT_EQ(rc, 0); EXPECT_EQ(imported->size(), cloud->size()); } // Тест: Повторное открытие проекта TEST_F(IntegrationTest, OpenCloseCycle) { // 1. Создаем и заполняем проект { ProjectManager pm1; bool result = pm1.newProject(projectDir, "Cycle Test"); ASSERT_TRUE(result); CloudPtr cloud = createTestCloud(50); pm1.addScan(cloud, "Cycle Scan"); result = pm1.saveProject(); ASSERT_TRUE(result); EXPECT_EQ(pm1.scanCount(), 1); } // 2. Закрываем и reopening { ProjectManager pm2; bool result = pm2.openProject(projectDir); ASSERT_TRUE(result); EXPECT_TRUE(pm2.isOpen()); EXPECT_EQ(pm2.scanCount(), 1); CloudPtr loaded = pm2.scanCloud(0); EXPECT_EQ(loaded->size(), 50); EXPECT_EQ(pm2.scans()[0].name, "Cycle Scan"); } } // Тест: Загрузка и экспорт PCD TEST_F(IntegrationTest, PCDFormat) { ProjectManager pm; bool result = pm.newProject(projectDir, "PCD Test"); ASSERT_TRUE(result); CloudPtr cloud = createTestCloud(100); pm.addScan(cloud, "PCD Cloud"); // Экспорт в PCD ExportManager em; QString exportPath = projectDir + "/cloud.pcd"; result = em.savePointCloud(cloud, exportPath, ExportManager::CloudFormat::PCD); ASSERT_TRUE(result); // Загрузка из PCD CloudPtr imported(new pcl::PointCloud<pcl::PointXYZRGB>); int rc = pcl::io::loadPCDFile(exportPath.toStdString(), *imported); EXPECT_EQ(rc, 0); EXPECT_EQ(imported->size(), cloud->size()); } // Тест: Быстрый цикл проект -> фильтр -> экспорт TEST_F(IntegrationTest, FastPipeline) { CloudPtr cloud = createTestCloud(5000); // Проверяем исходное облако ASSERT_GT(cloud->size(), 0u) << "Исходное облако пустое"; // Применяем фильтры PointCloudFilters filters; // Уменьшаем размер вокселя для более агрессивной децимации CloudPtr v1 = filters.applyVoxelGrid(cloud, 0.005f); EXPECT_GT(v1->size(), 0u) << "Облако стало пустым после VoxelGrid"; CloudPtr v2 = filters.applyRadiusOutlierRemoval(v1, 0.015, 5); EXPECT_GT(v2->size(), 0u) << "Облако стало пустым после RadiusOutlierRemoval"; CloudPtr v3 = filters.applyStatisticalOutlierRemoval(v2, 30, 1.0); // Проверяем, что облако не пустое ASSERT_GT(v3->size(), 0u) << "Облако стало пустым после фильтрации (SOR)"; // Экспорт ExportManager em; QString exportPath = projectDir + "/fast_pipeline.ply"; bool result = em.savePointCloud(v3, exportPath, ExportManager::CloudFormat::PLY); ASSERT_TRUE(result) << "Ошибка экспорта: " << em.lastError().toStdString(); // Проверка CloudPtr imported(new pcl::PointCloud<pcl::PointXYZRGB>); int rc = pcl::io::loadPLYFile(exportPath.toStdString(), *imported); EXPECT_EQ(rc, 0) << "Ошибка загрузки файла: " << exportPath.toStdString(); EXPECT_LE(imported->size(), cloud->size()); EXPECT_GT(imported->size(), 0u); } // Тест: Множественные экспорт форматы TEST_F(IntegrationTest, MultipleFormats) { CloudPtr cloud = createTestCloud(100); QString exportBase = projectDir + "/test_cloud"; // PLY ExportManager em; QString plyPath = exportBase + ".ply"; bool result = em.savePointCloud(cloud, plyPath, ExportManager::CloudFormat::PLY); ASSERT_TRUE(result); // PCD QString pcdPath = exportBase + ".pcd"; result = em.savePointCloud(cloud, pcdPath, ExportManager::CloudFormat::PCD); ASSERT_TRUE(result); // Загружаем оба формата CloudPtr plyCloud(new pcl::PointCloud<pcl::PointXYZRGB>); int rc = pcl::io::loadPLYFile(plyPath.toStdString(), *plyCloud); EXPECT_EQ(rc, 0); EXPECT_EQ(plyCloud->size(), cloud->size()); CloudPtr pcdCloud(new pcl::PointCloud<pcl::PointXYZRGB>); rc = pcl::io::loadPCDFile(pcdPath.toStdString(), *pcdCloud); EXPECT_EQ(rc, 0); EXPECT_EQ(pcdCloud->size(), cloud->size()); } // Тест: Обработка пустого облака TEST_F(IntegrationTest, EmptyCloudHandling) { ProjectManager pm; bool result = pm.newProject(projectDir, "Empty Test"); ASSERT_TRUE(result); CloudPtr empty(new pcl::PointCloud<pcl::PointXYZRGB>); // Добавление пустого скана должно завершиться с ошибкой int rc = pcl::io::savePLYFile((projectDir + "/empty.ply").toStdString(), *empty); EXPECT_NE(rc, 0); // PCL возвращает ошибку для пустого облака } // Тест: Проверка сохранения метаданных TEST_F(IntegrationTest, MetadataPreservation) { ProjectManager pm1; bool result = pm1.newProject(projectDir, "Metadata Test"); ASSERT_TRUE(result); // Добавляем сканы с разными именами for (int i = 0; i < 5; ++i) { CloudPtr cloud = createTestCloud(10 + i * 10); pm1.addScan(cloud, "Scan_" + QString::number(i)); } result = pm1.saveProject(); ASSERT_TRUE(result); // Проверяем JSON файл QString jsonPath = projectDir + "/project.json"; EXPECT_TRUE(QFileInfo::exists(jsonPath)); // Читаем метаданные QFile jsonFile(jsonPath); ASSERT_TRUE(jsonFile.open(QIODevice::ReadOnly)); QByteArray jsonData = jsonFile.readAll(); jsonFile.close(); QJsonDocument doc = QJsonDocument::fromJson(jsonData); ASSERT_FALSE(doc.isNull()); QJsonObject rootObj = doc.object(); EXPECT_EQ(rootObj["name"].toString(), "Metadata Test"); QJsonArray scansArray = rootObj["scans"].toArray(); EXPECT_EQ(scansArray.size(), 5); // Проверяем имена сканов for (int i = 0; i < 5; ++i) { QJsonObject scanObj = scansArray[i].toObject(); EXPECT_EQ(scanObj["name"].toString(), "Scan_" + QString::number(i)); } }