193 lines
6.5 KiB
C++
193 lines
6.5 KiB
C++
|
|
#include "TestCommon.h"
|
||
|
|
#include "ANSCustomFireNSmoke.h"
|
||
|
|
|
||
|
|
// ===========================================================================
|
||
|
|
// Unit Tests — no model files required
|
||
|
|
// ===========================================================================
|
||
|
|
|
||
|
|
class FireNSmokeUnitTest : public ::testing::Test {
|
||
|
|
protected:
|
||
|
|
ANSCustomFS detector;
|
||
|
|
};
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeUnitTest, EmptyFrameReturnsNoDetections) {
|
||
|
|
cv::Mat empty;
|
||
|
|
auto results = detector.RunInference(empty);
|
||
|
|
EXPECT_TRUE(results.empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeUnitTest, TinyFrameReturnsNoDetections) {
|
||
|
|
cv::Mat tiny = TestUtils::CreateTestFrame(5, 5);
|
||
|
|
auto results = detector.RunInference(tiny);
|
||
|
|
EXPECT_TRUE(results.empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeUnitTest, UninitializedDetectorReturnsNoDetections) {
|
||
|
|
cv::Mat frame = TestUtils::CreateTestFrame(640, 480);
|
||
|
|
auto results = detector.RunInference(frame);
|
||
|
|
EXPECT_TRUE(results.empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeUnitTest, RunInferenceWithCameraId) {
|
||
|
|
cv::Mat frame = TestUtils::CreateTestFrame(640, 480);
|
||
|
|
auto results = detector.RunInference(frame, "test_cam_01");
|
||
|
|
EXPECT_TRUE(results.empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeUnitTest, ConfigureParametersReturnsValidConfig) {
|
||
|
|
CustomParams params;
|
||
|
|
bool result = detector.ConfigureParameters(params);
|
||
|
|
EXPECT_TRUE(result);
|
||
|
|
|
||
|
|
// Should have ExclusiveROIs ROI config
|
||
|
|
ASSERT_FALSE(params.ROI_Config.empty());
|
||
|
|
EXPECT_EQ(params.ROI_Config[0].Name, "ExclusiveROIs");
|
||
|
|
EXPECT_TRUE(params.ROI_Config[0].Rectangle);
|
||
|
|
EXPECT_FALSE(params.ROI_Config[0].Polygon);
|
||
|
|
EXPECT_FALSE(params.ROI_Config[0].Line);
|
||
|
|
EXPECT_EQ(params.ROI_Config[0].MinItems, 0);
|
||
|
|
EXPECT_EQ(params.ROI_Config[0].MaxItems, 20);
|
||
|
|
|
||
|
|
// Should have SmokeScore and Sensitivity parameters
|
||
|
|
ASSERT_GE(params.Parameters.size(), 2u);
|
||
|
|
|
||
|
|
bool hasSmokeScore = false;
|
||
|
|
bool hasSensitivity = false;
|
||
|
|
for (const auto& p : params.Parameters) {
|
||
|
|
if (p.Name == "SmokeScore") {
|
||
|
|
hasSmokeScore = true;
|
||
|
|
EXPECT_EQ(p.DataType, "float");
|
||
|
|
EXPECT_EQ(p.MaxValue, 1);
|
||
|
|
EXPECT_EQ(p.MinValue, 0);
|
||
|
|
}
|
||
|
|
if (p.Name == "Sensitivity") {
|
||
|
|
hasSensitivity = true;
|
||
|
|
EXPECT_EQ(p.DataType, "float");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
EXPECT_TRUE(hasSmokeScore) << "Missing SmokeScore parameter";
|
||
|
|
EXPECT_TRUE(hasSensitivity) << "Missing Sensitivity parameter";
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeUnitTest, DestroySucceeds) {
|
||
|
|
EXPECT_TRUE(detector.Destroy());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeUnitTest, DestroyCanBeCalledMultipleTimes) {
|
||
|
|
EXPECT_TRUE(detector.Destroy());
|
||
|
|
EXPECT_TRUE(detector.Destroy());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeUnitTest, InitializeWithInvalidDirectoryFails) {
|
||
|
|
std::string labelMap;
|
||
|
|
bool result = detector.Initialize("C:\\NonExistent\\Path\\Model", 0.5f, labelMap);
|
||
|
|
EXPECT_FALSE(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeUnitTest, OptimizeBeforeInitializeReturnsFalse) {
|
||
|
|
EXPECT_FALSE(detector.OptimizeModel(true));
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===========================================================================
|
||
|
|
// Integration Tests — require model files on disk
|
||
|
|
// ===========================================================================
|
||
|
|
|
||
|
|
class FireNSmokeIntegrationTest : public ::testing::Test {
|
||
|
|
protected:
|
||
|
|
ANSCustomFS detector;
|
||
|
|
std::string labelMap;
|
||
|
|
std::vector<std::string> classes;
|
||
|
|
|
||
|
|
void SetUp() override {
|
||
|
|
if (!TestConfig::ModelExists(TestConfig::FIRE_SMOKE_MODEL_DIR)) {
|
||
|
|
GTEST_SKIP() << "Fire/Smoke model not found at: " << TestConfig::FIRE_SMOKE_MODEL_DIR;
|
||
|
|
}
|
||
|
|
bool ok = detector.Initialize(TestConfig::FIRE_SMOKE_MODEL_DIR, 0.5f, labelMap);
|
||
|
|
ASSERT_TRUE(ok) << "Failed to initialize Fire/Smoke detector";
|
||
|
|
classes = TestUtils::ParseLabelMap(labelMap);
|
||
|
|
}
|
||
|
|
|
||
|
|
void TearDown() override {
|
||
|
|
detector.Destroy();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeIntegrationTest, InitializeProducesLabelMap) {
|
||
|
|
EXPECT_FALSE(labelMap.empty());
|
||
|
|
EXPECT_FALSE(classes.empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeIntegrationTest, InferenceOnSolidFrameReturnsNoDetections) {
|
||
|
|
cv::Mat frame = TestUtils::CreateTestFrame(1920, 1080);
|
||
|
|
auto results = detector.RunInference(frame, "test_cam");
|
||
|
|
EXPECT_TRUE(results.empty()) << "Solid gray frame should not trigger fire/smoke";
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeIntegrationTest, InferenceOnSmallFrame) {
|
||
|
|
cv::Mat frame = TestUtils::CreateTestFrame(320, 240);
|
||
|
|
auto results = detector.RunInference(frame, "test_cam");
|
||
|
|
SUCCEED();
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeIntegrationTest, InferenceOnLargeFrame) {
|
||
|
|
cv::Mat frame = TestUtils::CreateTestFrame(3840, 2160);
|
||
|
|
auto results = detector.RunInference(frame, "test_cam");
|
||
|
|
SUCCEED();
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeIntegrationTest, DetectionResultFieldsAreValid) {
|
||
|
|
if (!TestConfig::VideoExists(TestConfig::FIRE_SMOKE_VIDEO)) {
|
||
|
|
GTEST_SKIP() << "Fire/Smoke test video not found";
|
||
|
|
}
|
||
|
|
|
||
|
|
cv::VideoCapture cap(TestConfig::FIRE_SMOKE_VIDEO);
|
||
|
|
ASSERT_TRUE(cap.isOpened());
|
||
|
|
|
||
|
|
bool detectionFound = false;
|
||
|
|
for (int i = 0; i < 300 && !detectionFound; i++) {
|
||
|
|
cv::Mat frame;
|
||
|
|
if (!cap.read(frame)) break;
|
||
|
|
|
||
|
|
auto results = detector.RunInference(frame, "test_cam");
|
||
|
|
for (const auto& obj : results) {
|
||
|
|
detectionFound = true;
|
||
|
|
EXPECT_GE(obj.confidence, 0.0f);
|
||
|
|
EXPECT_LE(obj.confidence, 1.0f);
|
||
|
|
EXPECT_GE(obj.box.width, 0);
|
||
|
|
EXPECT_GE(obj.box.height, 0);
|
||
|
|
EXPECT_TRUE(obj.classId == 0 || obj.classId == 2)
|
||
|
|
<< "Expected fire (0) or smoke (2), got classId=" << obj.classId;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
cap.release();
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeIntegrationTest, PerformanceBenchmark) {
|
||
|
|
if (!TestConfig::VideoExists(TestConfig::FIRE_SMOKE_VIDEO)) {
|
||
|
|
GTEST_SKIP() << "Fire/Smoke test video not found";
|
||
|
|
}
|
||
|
|
|
||
|
|
auto [totalDetections, avgMs] = TestUtils::RunVideoFrames(detector, TestConfig::FIRE_SMOKE_VIDEO, 100);
|
||
|
|
ASSERT_GE(totalDetections, 0) << "Video could not be opened";
|
||
|
|
|
||
|
|
std::cout << "[FireNSmoke] 100 frames: avg=" << avgMs << "ms/frame, "
|
||
|
|
<< "detections=" << totalDetections << std::endl;
|
||
|
|
|
||
|
|
EXPECT_LT(avgMs, 200.0) << "Average inference time exceeds 200ms";
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(FireNSmokeIntegrationTest, ThreadSafetyConcurrentInference) {
|
||
|
|
cv::Mat frame1 = TestUtils::CreateTestFrame(640, 480, cv::Scalar(100, 100, 100));
|
||
|
|
cv::Mat frame2 = TestUtils::CreateTestFrame(640, 480, cv::Scalar(200, 200, 200));
|
||
|
|
|
||
|
|
std::vector<CustomObject> results1, results2;
|
||
|
|
|
||
|
|
std::thread t1([&]() { results1 = detector.RunInference(frame1, "cam_1"); });
|
||
|
|
std::thread t2([&]() { results2 = detector.RunInference(frame2, "cam_2"); });
|
||
|
|
|
||
|
|
t1.join();
|
||
|
|
t2.join();
|
||
|
|
|
||
|
|
SUCCEED();
|
||
|
|
}
|