videoIO seperation
This commit is contained in:
148
tests/VideoOutputThreadTests.cpp
Normal file
148
tests/VideoOutputThreadTests.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "VideoOutputThread.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
namespace
|
||||
{
|
||||
int gFailures = 0;
|
||||
|
||||
void Expect(bool condition, const char* message)
|
||||
{
|
||||
if (condition)
|
||||
return;
|
||||
std::cerr << "FAIL: " << message << "\n";
|
||||
++gFailures;
|
||||
}
|
||||
|
||||
struct FakeExchangeMetrics
|
||||
{
|
||||
std::size_t scheduledCount = 0;
|
||||
};
|
||||
|
||||
class FakeExchange
|
||||
{
|
||||
public:
|
||||
FakeExchange()
|
||||
{
|
||||
mFrame.bytes = mBytes;
|
||||
mFrame.rowBytes = 8;
|
||||
mFrame.width = 2;
|
||||
mFrame.height = 2;
|
||||
mFrame.pixelFormat = VideoIOPixelFormat::Bgra8;
|
||||
}
|
||||
|
||||
FakeExchangeMetrics Metrics() const
|
||||
{
|
||||
FakeExchangeMetrics metrics;
|
||||
metrics.scheduledCount = mScheduled.load();
|
||||
return metrics;
|
||||
}
|
||||
|
||||
bool ConsumeCompletedForSchedule(SystemFrame& frame)
|
||||
{
|
||||
bool expected = true;
|
||||
if (!mHasFrame.compare_exchange_strong(expected, false))
|
||||
return false;
|
||||
frame = mFrame;
|
||||
mScheduled.fetch_add(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReleaseScheduledByBytes(void*)
|
||||
{
|
||||
mReleased.fetch_add(1);
|
||||
}
|
||||
|
||||
std::atomic<std::size_t> mScheduled{ 0 };
|
||||
std::atomic<std::size_t> mReleased{ 0 };
|
||||
|
||||
private:
|
||||
std::atomic<bool> mHasFrame{ true };
|
||||
unsigned char mBytes[16] = {};
|
||||
SystemFrame mFrame;
|
||||
};
|
||||
|
||||
class FakeOutputEdge final : public RenderCadenceCompositor::IVideoOutputEdge
|
||||
{
|
||||
public:
|
||||
bool StartScheduledPlayback(std::string&) override { return true; }
|
||||
bool ScheduleFrame(const VideoIOOutputFrame& frame) override
|
||||
{
|
||||
mLastWidth = frame.width;
|
||||
mScheduled.fetch_add(1);
|
||||
return mScheduleResult;
|
||||
}
|
||||
void Stop() override {}
|
||||
void ReleaseResources() override {}
|
||||
const VideoIOState& State() const override { return mState; }
|
||||
RenderCadenceCompositor::VideoOutputEdgeMetrics Metrics() const override { return mMetrics; }
|
||||
|
||||
bool mScheduleResult = true;
|
||||
std::atomic<std::size_t> mScheduled{ 0 };
|
||||
unsigned mLastWidth = 0;
|
||||
|
||||
private:
|
||||
VideoIOState mState;
|
||||
RenderCadenceCompositor::VideoOutputEdgeMetrics mMetrics;
|
||||
};
|
||||
|
||||
void TestSchedulesCompletedFrameThroughGenericEdge()
|
||||
{
|
||||
FakeExchange exchange;
|
||||
FakeOutputEdge output;
|
||||
RenderCadenceCompositor::VideoOutputThreadConfig config;
|
||||
config.targetBufferedFrames = 1;
|
||||
config.idleSleep = std::chrono::milliseconds(1);
|
||||
|
||||
RenderCadenceCompositor::VideoOutputThread<FakeExchange> thread(output, exchange, config);
|
||||
Expect(thread.Start(), "video output thread starts");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
thread.Stop();
|
||||
|
||||
const auto metrics = thread.Metrics();
|
||||
Expect(output.mScheduled.load() == 1, "generic output edge receives one scheduled frame");
|
||||
Expect(output.mLastWidth == 2, "scheduled frame data comes from exchange");
|
||||
Expect(metrics.scheduledFrames == 1, "thread scheduled frame metric increments");
|
||||
Expect(metrics.scheduleFailures == 0, "no schedule failure is reported");
|
||||
Expect(exchange.mReleased.load() == 0, "successful schedule keeps frame scheduled");
|
||||
}
|
||||
|
||||
void TestReleasesFrameWhenGenericEdgeRejectsSchedule()
|
||||
{
|
||||
FakeExchange exchange;
|
||||
FakeOutputEdge output;
|
||||
output.mScheduleResult = false;
|
||||
RenderCadenceCompositor::VideoOutputThreadConfig config;
|
||||
config.targetBufferedFrames = 1;
|
||||
config.idleSleep = std::chrono::milliseconds(1);
|
||||
|
||||
RenderCadenceCompositor::VideoOutputThread<FakeExchange> thread(output, exchange, config);
|
||||
Expect(thread.Start(), "video output thread starts for failure path");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
thread.Stop();
|
||||
|
||||
const auto metrics = thread.Metrics();
|
||||
Expect(output.mScheduled.load() == 1, "generic output edge receives failed schedule attempt");
|
||||
Expect(metrics.scheduledFrames == 0, "failed schedule does not increment scheduled frames");
|
||||
Expect(metrics.scheduleFailures == 1, "schedule failure metric increments");
|
||||
Expect(exchange.mReleased.load() == 1, "failed schedule releases the frame back to exchange");
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
TestSchedulesCompletedFrameThroughGenericEdge();
|
||||
TestReleasesFrameWhenGenericEdgeRejectsSchedule();
|
||||
|
||||
if (gFailures != 0)
|
||||
{
|
||||
std::cerr << gFailures << " VideoOutputThread test failure(s).\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "VideoOutputThread tests passed.\n";
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user