# ๐Ÿงช Unit Test ๊ตฌํ˜„ ๊ณ„ํš - 2025-09-23 ๊ฐœ์ •ํŒ **โœ… ์ „์ œ ์กฐ๊ฑด**: MAJOR_REFACTORING_GUIDE ์™„๋ฃŒ, GPU ํŒŒ์ดํ”„๋ผ์ธ ์žฌ์„ค๊ณ„ ์™„๋ฃŒ **๐ŸŽฏ ํ˜„์žฌ ๋ชฉํ‘œ**: ๋‹จ์ˆœํ™”๋œ ์•„ํ‚คํ…์ฒ˜ (734์ค„)์— ๋Œ€ํ•œ ํฌ๊ด„์  Unit Test ๊ตฌํ˜„ --- ## ๐Ÿ” **ํ˜„์žฌ ์ƒํ™ฉ (2025-09-23)** ### **โœ… ์™„๋ฃŒ๋œ ์ž‘์—…** - **MAJOR_REFACTORING_GUIDE**: ๋ณต์žกํ•œ ํŒŒ์ดํ”„๋ผ์ธ ์ œ๊ฑฐ, ProcessSingleFrame() 25์ค„ ๋‹จ์ˆœํ™” - **GPU ํŒŒ์ดํ”„๋ผ์ธ**: SimpleGPURenderer ๊ตฌํ˜„, CPU-GPU ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•„ํ‚คํ…์ฒ˜ - **๊ธฐ๋ณธ Unit Test**: VideoTypesTest.cpp ์™„๋ฃŒ - **ํ—ค๋“œ๋ฆฌ์Šค ํ”„๋กœ์ ํŠธ**: ๋นŒ๋“œ ๋ฐ ์‹คํ–‰ ์„ฑ๊ณต ### **๐Ÿ“‹ ํ˜„์žฌ Unit Test ๊ตฌ์กฐ** ``` D:\Project\video-av1\vav2\Vav2Player\Vav2Player\ โ”œโ”€โ”€ Vav2UnitTest.vcxproj # โœ… ๊ธฐ์กด ํ…Œ์ŠคํŠธ ํ”„๋กœ์ ํŠธ โ”œโ”€โ”€ unit-test/ โ”‚ โ”œโ”€โ”€ pch.h / pch.cpp # โœ… ํ…Œ์ŠคํŠธ ์ „์šฉ PCH โ”‚ โ””โ”€โ”€ VideoTypesTest.cpp # โœ… ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์ฒด ํ…Œ์ŠคํŠธ โ””โ”€โ”€ x64/Debug/UnitTest/ # โœ… ๋นŒ๋“œ ์ถœ๋ ฅ ``` ### **๐ŸŽฏ ์ถ”๊ฐ€ ํ•„์š”ํ•œ ํ…Œ์ŠคํŠธ** - **WebMFileReader**: ํŒŒ์ผ ํŒŒ์‹ฑ ๋กœ์ง (์‹ค์ œ ๊ตฌ์ฒด ํด๋ž˜์Šค ํ…Œ์ŠคํŠธ) - **AV1Decoder**: ๋””์ฝ”๋”ฉ ๋กœ์ง - **VideoDecoderFactory**: ํŒฉํ† ๋ฆฌ ํŒจํ„ด - **SimpleGPURenderer**: GPU ๋ Œ๋”๋ง (์Šคํ… ๋ชจ๋“œ) - **ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ**: ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ --- ## ๐Ÿ” **์ธํ„ฐํŽ˜์ด์Šค ๋ถ„์„ (Unit Test ์ „ ํ•„์ˆ˜ ๊ฒ€ํ† )** ### **ํ˜„์žฌ ์ธํ„ฐํŽ˜์ด์Šค ์ƒํ™ฉ** ```cpp โœ… IVideoDecoder.h // ์ด๋ฏธ ์ธํ„ฐํŽ˜์ด์Šคํ™” ์™„๋ฃŒ โŒ WebMFileReader.h // ๊ตฌ์ฒด ํด๋ž˜์Šค, ์ธํ„ฐํŽ˜์ด์Šค ์—†์Œ โŒ SimpleGPURenderer.h // ๊ตฌ์ฒด ํด๋ž˜์Šค, ์ธํ„ฐํŽ˜์ด์Šค ์—†์Œ โœ… VideoDecoderFactory.h // ํŒฉํ† ๋ฆฌ ํŒจํ„ด, ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฐ˜ ``` ### **๐Ÿ”ง Option A: ์ธํ„ฐํŽ˜์ด์Šค ์ถ”๊ฐ€ ๋ฆฌํŒฉํ† ๋ง (๊ถŒ์žฅ)** #### **A.1 IWebMFileReader ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ** ```cpp // src/FileIO/IWebMFileReader.h (์‹ ๊ทœ ์ƒ์„ฑ) #pragma once #include "../Common/VideoTypes.h" #include #include namespace Vav2Player { class IWebMFileReader { public: virtual ~IWebMFileReader() = default; // ํŒŒ์ผ ์—ด๊ธฐ/๋‹ซ๊ธฐ virtual bool OpenFile(const std::string& file_path) = 0; virtual void CloseFile() = 0; virtual bool IsFileOpen() const = 0; // ํŒŒ์ผ ๋ฐ ์ŠคํŠธ๋ฆผ ์ •๋ณด virtual const VideoMetadata& GetVideoMetadata() const = 0; virtual std::string GetFilePath() const = 0; // ๋น„๋””์˜ค ํŠธ๋ž™ ๊ด€๋ฆฌ virtual std::vector GetVideoTracks() const = 0; virtual bool SelectVideoTrack(int track_number) = 0; // ํŒจํ‚ท ์ฝ๊ธฐ virtual bool ReadNextPacket(VideoPacket& packet) = 0; virtual bool SeekToTime(double time_seconds) = 0; virtual bool SeekToFrame(uint64_t frame_number) = 0; virtual bool Reset() = 0; }; } ``` #### **A.2 WebMFileReader ๋ฆฌํŒฉํ† ๋ง** ```cpp // src/FileIO/WebMFileReader.h ์ˆ˜์ • class WebMFileReader : public IWebMFileReader { // ๊ธฐ์กด ๊ตฌํ˜„์„ virtual ๋ฉ”์„œ๋“œ๋กœ ๋ณ€๊ฒฝ // ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์†์œผ๋กœ ๋ณ€๊ฒฝ }; ``` #### **A.3 IVideoRenderer ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ** ```cpp // src/Rendering/IVideoRenderer.h (์‹ ๊ทœ ์ƒ์„ฑ) #pragma once #include "../Common/VideoTypes.h" #include namespace Vav2Player { class IVideoRenderer { public: virtual ~IVideoRenderer() = default; // ์ดˆ๊ธฐํ™” virtual HRESULT Initialize(uint32_t width, uint32_t height) = 0; virtual bool IsInitialized() const = 0; virtual void Cleanup() = 0; // ๋ Œ๋”๋ง virtual bool TryRenderFrame(const VideoFrame& frame) = 0; virtual HRESULT RenderVideoFrame(const VideoFrame& frame) = 0; // ์„ค์ • virtual void SetAspectFitMode(bool enabled) = 0; virtual void UpdateContainerSize(uint32_t width, uint32_t height) = 0; }; } ``` ### **๐Ÿš€ Option B: ์ธํ„ฐํŽ˜์ด์Šค ์—†์ด ์ง์ ‘ ํ…Œ์ŠคํŠธ (๋น ๋ฅธ ์‹œ์ž‘)** #### **B.1 ์žฅ์ ** - ์ฆ‰์‹œ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ์‹œ์ž‘ ๊ฐ€๋Šฅ - ๊ธฐ์กด ์ฝ”๋“œ ์ˆ˜์ • ์—†์Œ - ๋‹จ์ˆœํ•œ ์ ‘๊ทผ๋ฒ• #### **B.2 ๋‹จ์ ** - Mock ์ƒ์„ฑ ์–ด๋ ค์›€ - ์˜์กด์„ฑ ์ฃผ์ž… ๋ถˆ๊ฐ€๋Šฅ - ํ…Œ์ŠคํŠธ ๊ฒฉ๋ฆฌ ํ•œ๊ณ„ --- ## ๐Ÿ“‹ **๋‹จ๊ณ„๋ณ„ ๊ตฌํ˜„ ๊ณ„ํš** ### **๐Ÿ”„ Phase 0: ์ธํ„ฐํŽ˜์ด์Šค ๋ฆฌํŒฉํ† ๋ง (์„ ํƒ์ )** #### **0.1 ์ธํ„ฐํŽ˜์ด์Šค ํ•„์š”์„ฑ ํ‰๊ฐ€** ```cpp // ํ…Œ์ŠคํŠธ ๋ณต์žก๋„ vs ์ธํ„ฐํŽ˜์ด์Šค ๋„์ž… ๋น„์šฉ Component | Mock ํ•„์š”๋„ | ์ธํ„ฐํŽ˜์ด์Šค ์šฐ์„ ์ˆœ์œ„ --------------------|----------|---------------- WebMFileReader | ๋†’์Œ | โญโญโญ ๊ถŒ์žฅ SimpleGPURenderer | ์ค‘๊ฐ„ | โญโญ ์„ ํƒ์  VideoDecoderFactory | ๋‚ฎ์Œ | โญ ์ด๋ฏธ ์ถฉ๋ถ„ ``` #### **0.2 ์ธํ„ฐํŽ˜์ด์Šค ๋ฆฌํŒฉํ† ๋ง (์„ ํƒ ์‹œ)** 1. **IWebMFileReader.h ์ƒ์„ฑ** (2์‹œ๊ฐ„) 2. **WebMFileReader.h ์ˆ˜์ •** (1์‹œ๊ฐ„) 3. **IVideoRenderer.h ์ƒ์„ฑ** (1์‹œ๊ฐ„) 4. **SimpleGPURenderer.h ์ˆ˜์ •** (1์‹œ๊ฐ„) 5. **VideoPlayerControl.xaml.h ์˜์กด์„ฑ ์—…๋ฐ์ดํŠธ** (30๋ถ„) 6. **๋นŒ๋“œ ํ…Œ์ŠคํŠธ** (30๋ถ„) **์ด ์˜ˆ์ƒ ์‹œ๊ฐ„: 6์‹œ๊ฐ„** ### **๐Ÿ”ง Phase 1: ํ•ต์‹ฌ ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ (์šฐ์„ ์ˆœ์œ„ 1)** #### **1.1 WebMFileReader ํ…Œ์ŠคํŠธ (์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฐ˜)** ```cpp // unit-test/WebMFileReaderTest.cpp #include "pch.h" #include "../src/FileIO/WebMFileReader.h" // Option A๋ฅผ ์„ ํƒํ•œ ๊ฒฝ์šฐ: Mock์„ ์‚ฌ์šฉํ•œ ํ…Œ์ŠคํŠธ #ifdef USE_INTERFACE_APPROACH #include "../unit-test/mocks/MockWebMFileReader.h" #endif using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Vav2Player; TEST_CLASS(WebMFileReaderTest) { public: // Option A: ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฐ˜ ํ…Œ์ŠคํŠธ (Mock ์‚ฌ์šฉ ๊ฐ€๋Šฅ) TEST_METHOD(OpenFile_ValidFile_ShouldReturnTrue_WithMock) { #ifdef USE_INTERFACE_APPROACH // Arrange MockWebMFileReader mockReader; mockReader.SetOpenFileResult(true); // Act bool result = mockReader.OpenFile("test.webm"); // Assert Assert::IsTrue(result); #endif } // Option B: ์ง์ ‘ ํ…Œ์ŠคํŠธ (์ธํ„ฐํŽ˜์ด์Šค ์—†์ด) TEST_METHOD(Constructor_Default_ShouldInitializeCorrectly) { // Arrange & Act WebMFileReader reader; // Assert Assert::IsFalse(reader.IsFileOpen()); } TEST_METHOD(OpenFile_NonExistentFile_ShouldReturnFalse) { // Arrange WebMFileReader reader; std::string nonExistentFile = "nonexistent_file.webm"; // Act bool result = reader.OpenFile(nonExistentFile); // Assert Assert::IsFalse(result); } TEST_METHOD(OpenFile_EmptyFilename_ShouldReturnFalse) { // Arrange WebMFileReader reader; std::string emptyFile = ""; // Act bool result = reader.OpenFile(emptyFile); // Assert Assert::IsFalse(result); } }; ``` #### **1.2 AV1Decoder ํ…Œ์ŠคํŠธ** ```cpp // unit-test/AV1DecoderTest.cpp #include "pch.h" #include "../src/Decoder/AV1Decoder.h" TEST_CLASS(AV1DecoderTest) { public: TEST_METHOD(Constructor_Default_ShouldInitializeCorrectly) { // Arrange & Act AV1Decoder decoder; // Assert Assert::IsFalse(decoder.IsInitialized()); } TEST_METHOD(Initialize_ValidMetadata_ShouldReturnTrue) { // Arrange AV1Decoder decoder; VideoMetadata metadata; metadata.width = 1920; metadata.height = 1080; metadata.frame_rate = 30.0; metadata.codec_type = VideoCodecType::AV1; // Act bool result = decoder.Initialize(metadata); // Assert Assert::IsTrue(result); Assert::IsTrue(decoder.IsInitialized()); } TEST_METHOD(Initialize_InvalidCodecType_ShouldReturnFalse) { // Arrange AV1Decoder decoder; VideoMetadata metadata; metadata.codec_type = VideoCodecType::VP9; // Wrong codec // Act bool result = decoder.Initialize(metadata); // Assert Assert::IsFalse(result); } }; ``` #### **1.3 VideoDecoderFactory ํ…Œ์ŠคํŠธ** ```cpp // unit-test/VideoDecoderFactoryTest.cpp #include "pch.h" #include "../src/Decoder/VideoDecoderFactory.h" TEST_CLASS(VideoDecoderFactoryTest) { public: TEST_METHOD(CreateDecoder_AV1Software_ShouldReturnAV1Decoder) { // Act auto decoder = VideoDecoderFactory::CreateDecoder( VideoCodecType::AV1, VideoDecoderFactory::DecoderType::SOFTWARE ); // Assert Assert::IsNotNull(decoder.get()); } TEST_METHOD(CreateDecoder_AV1Auto_ShouldReturnDecoder) { // Act auto decoder = VideoDecoderFactory::CreateDecoder( VideoCodecType::AV1, VideoDecoderFactory::DecoderType::AUTO ); // Assert Assert::IsNotNull(decoder.get()); } TEST_METHOD(IsCodecSupported_AV1_ShouldReturnTrue) { // Act bool isSupported = VideoDecoderFactory::IsCodecSupported(VideoCodecType::AV1); // Assert Assert::IsTrue(isSupported); } }; ``` ### **๐Ÿ”ง Phase 1.5: Mock ์‹œ์Šคํ…œ ๊ตฌํ˜„ (Option A ์„ ํƒ ์‹œ)** #### **1.5.1 MockWebMFileReader ๊ตฌํ˜„** ```cpp // unit-test/mocks/MockWebMFileReader.h #pragma once #include "../../src/FileIO/IWebMFileReader.h" #include #include namespace Vav2Player { class MockWebMFileReader : public IWebMFileReader { private: bool m_openFileResult = true; bool m_isFileOpen = false; VideoMetadata m_metadata; std::vector m_testPackets; size_t m_currentPacketIndex = 0; public: // Mock ์„ค์ • ๋ฉ”์„œ๋“œ๋“ค void SetOpenFileResult(bool result) { m_openFileResult = result; } void SetMetadata(const VideoMetadata& metadata) { m_metadata = metadata; } void AddTestPacket(const VideoPacket& packet) { m_testPackets.push_back(packet); } void ResetPackets() { m_testPackets.clear(); m_currentPacketIndex = 0; } // IWebMFileReader ๊ตฌํ˜„ bool OpenFile(const std::string& file_path) override { m_isFileOpen = m_openFileResult; return m_openFileResult; } void CloseFile() override { m_isFileOpen = false; m_currentPacketIndex = 0; } bool IsFileOpen() const override { return m_isFileOpen; } const VideoMetadata& GetVideoMetadata() const override { return m_metadata; } std::string GetFilePath() const override { return "mock_file.webm"; } std::vector GetVideoTracks() const override { std::vector tracks; VideoTrackInfo track; track.track_number = 1; track.codec_type = VideoCodecType::AV1; track.width = 1920; track.height = 1080; tracks.push_back(track); return tracks; } bool SelectVideoTrack(int track_number) override { return track_number == 1; } bool ReadNextPacket(VideoPacket& packet) override { if (m_currentPacketIndex >= m_testPackets.size()) { return false; } packet = m_testPackets[m_currentPacketIndex++]; return true; } bool SeekToTime(double time_seconds) override { m_currentPacketIndex = 0; return true; } bool SeekToFrame(uint64_t frame_number) override { m_currentPacketIndex = static_cast(frame_number); return m_currentPacketIndex < m_testPackets.size(); } bool Reset() override { m_currentPacketIndex = 0; return true; } }; } ``` #### **1.5.2 MockVideoRenderer ๊ตฌํ˜„** ```cpp // unit-test/mocks/MockVideoRenderer.h #pragma once #include "../../src/Rendering/IVideoRenderer.h" namespace Vav2Player { class MockVideoRenderer : public IVideoRenderer { private: bool m_initialized = false; bool m_tryRenderResult = true; HRESULT m_renderResult = S_OK; uint32_t m_width = 0; uint32_t m_height = 0; public: // Mock ์„ค์ • void SetTryRenderResult(bool result) { m_tryRenderResult = result; } void SetRenderResult(HRESULT result) { m_renderResult = result; } // ํ…Œ์ŠคํŠธ ๊ฒ€์ฆ์šฉ uint32_t GetLastWidth() const { return m_width; } uint32_t GetLastHeight() const { return m_height; } // IVideoRenderer ๊ตฌํ˜„ HRESULT Initialize(uint32_t width, uint32_t height) override { m_width = width; m_height = height; m_initialized = (width > 0 && height > 0); return m_initialized ? S_OK : E_INVALIDARG; } bool IsInitialized() const override { return m_initialized; } void Cleanup() override { m_initialized = false; m_width = 0; m_height = 0; } bool TryRenderFrame(const VideoFrame& frame) override { return m_initialized && m_tryRenderResult; } HRESULT RenderVideoFrame(const VideoFrame& frame) override { return m_initialized ? m_renderResult : E_NOT_VALID_STATE; } void SetAspectFitMode(bool enabled) override { // Mock implementation - do nothing } void UpdateContainerSize(uint32_t width, uint32_t height) override { // Mock implementation - store values m_width = width; m_height = height; } }; } ``` ### **โšก Phase 2: GPU ๋ Œ๋”๋ง ํ…Œ์ŠคํŠธ** #### **2.1 SimpleGPURenderer ํ…Œ์ŠคํŠธ (์Šคํ… ๋ชจ๋“œ)** ```cpp // unit-test/SimpleGPURendererTest.cpp #include "pch.h" #include "../src/Rendering/SimpleGPURenderer.h" TEST_CLASS(SimpleGPURendererTest) { public: TEST_METHOD(Constructor_Default_ShouldInitializeCorrectly) { // Arrange & Act SimpleGPURenderer renderer; // Assert Assert::IsFalse(renderer.IsInitialized()); } TEST_METHOD(Initialize_ValidDimensions_ShouldReturnTrue) { // Arrange SimpleGPURenderer renderer; // Act HRESULT hr = renderer.Initialize(1920, 1080); // Assert // Note: May fail in headless environment, but should not crash // Success depends on D3D12 availability Assert::IsTrue(SUCCEEDED(hr) || hr == DXGI_ERROR_UNSUPPORTED); } }; ``` ### **๐Ÿงช Phase 3: ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ** #### **3.1 ๊ฐ„๋‹จํ•œ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ** ```cpp // unit-test/IntegrationTest.cpp #include "pch.h" #include "../src/FileIO/WebMFileReader.h" #include "../src/Decoder/VideoDecoderFactory.h" TEST_CLASS(IntegrationTest) { public: TEST_METHOD(BasicPipeline_ComponentCreation_ShouldWork) { // Arrange & Act auto fileReader = std::make_unique(); auto decoder = VideoDecoderFactory::CreateDecoder( VideoCodecType::AV1, VideoDecoderFactory::DecoderType::SOFTWARE ); // Assert Assert::IsNotNull(fileReader.get()); Assert::IsNotNull(decoder.get()); } TEST_METHOD(VideoFrame_AllocationAndDeallocation_ShouldNotLeak) { // Memory leak test for (int i = 0; i < 100; i++) { VideoFrame frame; bool result = frame.AllocateYUV420P(1920, 1080); Assert::IsTrue(result); } // Frames should automatically deallocate } }; ``` --- ## ๐Ÿ“ **์ตœ์ข… ํ…Œ์ŠคํŠธ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ** ### **๋ชฉํ‘œ ๊ตฌ์กฐ (Phase 3 ์™„๋ฃŒ ํ›„)** ``` D:\Project\video-av1\vav2\Vav2Player\Vav2Player\ โ”œโ”€โ”€ Vav2UnitTest.vcxproj โ”œโ”€โ”€ unit-test/ โ”‚ โ”œโ”€โ”€ pch.h / pch.cpp # โœ… ๊ธฐ์กด โ”‚ โ”œโ”€โ”€ VideoTypesTest.cpp # โœ… ๊ธฐ์กด โ”‚ โ”œโ”€โ”€ WebMFileReaderTest.cpp # ๐Ÿ†• Phase 1 โ”‚ โ”œโ”€โ”€ AV1DecoderTest.cpp # ๐Ÿ†• Phase 1 โ”‚ โ”œโ”€โ”€ VideoDecoderFactoryTest.cpp # ๐Ÿ†• Phase 1 โ”‚ โ”œโ”€โ”€ SimpleGPURendererTest.cpp # ๐Ÿ†• Phase 2 โ”‚ โ””โ”€โ”€ IntegrationTest.cpp # ๐Ÿ†• Phase 3 โ””โ”€โ”€ x64/Debug/UnitTest/ ``` ### **ํ”„๋กœ์ ํŠธ ํŒŒ์ผ ์—…๋ฐ์ดํŠธ** ```xml ``` --- ## โš™๏ธ **๊ตฌํ˜„ ์ƒ์„ธ** ### **1. Mock ์—†๋Š” ์‹ค์ œ ํด๋ž˜์Šค ํ…Œ์ŠคํŠธ** - **์ ‘๊ทผ๋ฒ•**: ๊ตฌ์ฒด ํด๋ž˜์Šค๋ฅผ ์ง์ ‘ ํ…Œ์ŠคํŠธ, Mock ๋ณต์žก์„ฑ ์ œ๊ฑฐ - **์™ธ๋ถ€ ์˜์กด์„ฑ**: ์‹คํŒจ ์ผ€์ด์Šค ํ…Œ์ŠคํŠธ๋กœ ๊ฒฉ๋ฆฌ (์กด์žฌํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ ๋“ฑ) - **GPU ์˜์กด์„ฑ**: ์‹คํŒจํ•ด๋„ ํฌ๋ž˜์‹œํ•˜์ง€ ์•Š๋Š”์ง€ ๊ฒ€์ฆ ### **2. ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ตœ์†Œํ™”** - **ํŒŒ์ผ ์˜์กด์„ฑ**: ์‹ค์ œ ํŒŒ์ผ ๋Œ€์‹  ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ๋ช…์œผ๋กœ ์—๋Ÿฌ ์ผ€์ด์Šค ํ…Œ์ŠคํŠธ - **๋ฉ”๋ชจ๋ฆฌ ํ…Œ์ŠคํŠธ**: ๋™์  ํ• ๋‹น/ํ•ด์ œ ๋ฐ˜๋ณต์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฒ€์ฆ - **๋‹จ์ˆœํ•œ ์ž…๋ ฅ**: ๊ธฐ๋ณธ๊ฐ’๊ณผ ๊ฒฝ๊ณ„๊ฐ’์„ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ ### **3. CI/CD ํ†ตํ•ฉ** ```bash # ์ž๋™ ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ cd "D:\Project\video-av1\vav2\Vav2Player\Vav2Player" # 1. ์œ ๋‹› ํ…Œ์ŠคํŠธ ๋นŒ๋“œ MSBuild Vav2UnitTest.vcxproj //p:Configuration=Debug //p:Platform=x64 //v:minimal # 2. ํ…Œ์ŠคํŠธ ์‹คํ–‰ vstest.console.exe "x64\Debug\UnitTest\Vav2UnitTest.dll" ``` --- ## ๐Ÿค” **์˜์‚ฌ๊ฒฐ์ • ๊ฐ€์ด๋“œ** ### **Option A vs Option B ์„ ํƒ ๊ธฐ์ค€** #### **Option A ์„ ํƒ (์ธํ„ฐํŽ˜์ด์Šค + Mock)** **๋‹ค์Œ ์กฐ๊ฑด ์ค‘ ํ•˜๋‚˜๋ผ๋„ ํ•ด๋‹นํ•˜๋ฉด ๊ถŒ์žฅ:** - ์˜์กด์„ฑ ์ฃผ์ž… ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ - ๋ณต์žกํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ - ์™ธ๋ถ€ ์˜์กด์„ฑ ๊ฒฉ๋ฆฌ๊ฐ€ ์ค‘์š”ํ•œ ๊ฒฝ์šฐ - ์žฅ๊ธฐ์ ์ธ ํ…Œ์ŠคํŠธ ํ™•์žฅ์„ฑ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ **์˜ˆ์ƒ ์‹œ๊ฐ„**: Phase 0 (6์‹œ๊ฐ„) + Phase 1-3 (4์ผ) = **5์ผ** #### **Option B ์„ ํƒ (์ง์ ‘ ํ…Œ์ŠคํŠธ)** **๋‹ค์Œ ์กฐ๊ฑด์— ํ•ด๋‹นํ•˜๋ฉด ๊ถŒ์žฅ:** - ๋น ๋ฅธ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„์ด ์šฐ์„ ์ธ ๊ฒฝ์šฐ - ๊ธฐ์กด ์ฝ”๋“œ ์ˆ˜์ •์„ ์ตœ์†Œํ™”ํ•˜๋ ค๋Š” ๊ฒฝ์šฐ - ๋‹จ์ˆœํ•œ ๊ธฐ๋Šฅ ๊ฒ€์ฆ๋งŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ - ๋ฆฌ์†Œ์Šค(์‹œ๊ฐ„/์ธ๋ ฅ)๊ฐ€ ์ œํ•œ์ ์ธ ๊ฒฝ์šฐ **์˜ˆ์ƒ ์‹œ๊ฐ„**: Phase 1-3 (4์ผ) = **4์ผ** ### **๊ถŒ์žฅ์‚ฌํ•ญ** ``` ํ˜„์žฌ ์ƒํ™ฉ ๊ณ ๋ ค ์‹œ ๐ŸŽฏ Option A ๊ถŒ์žฅ ์ด์œ : 1. WebMFileReader๋Š” ํŒŒ์ผ I/O ์˜์กด์„ฑ์ด ๋†’์Œ 2. GPU ๋ Œ๋”๋ง์€ ํ™˜๊ฒฝ ์˜์กด์„ฑ์ด ๋†’์Œ 3. ์žฅ๊ธฐ์ ์ธ ์œ ์ง€๋ณด์ˆ˜์„ฑ ํ•„์š” 4. MAJOR_REFACTORING_GUIDE ์™„๋ฃŒ๋กœ ์•ˆ์ •์ ์ธ ๊ธฐ๋ฐ˜ ํ™•๋ณด๋จ ``` ## ๐Ÿ• **์‹คํ–‰ ์Šค์ผ€์ค„** ### **Option A ์„ ํƒ ์‹œ (๊ถŒ์žฅ)** - **Phase 0**: 1์ผ (์ธํ„ฐํŽ˜์ด์Šค ๋ฆฌํŒฉํ† ๋ง) - **Phase 1.5**: 1์ผ (Mock ์‹œ์Šคํ…œ) - **Phase 1**: 2์ผ (ํ•ต์‹ฌ ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ) - **Phase 2**: 1์ผ (GPU ๋ Œ๋”๋ง ํ…Œ์ŠคํŠธ) - **Phase 3**: 1์ผ (ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ) **์ด 6์ผ** ### **Option B ์„ ํƒ ์‹œ** - **Phase 1**: 2-3์ผ (ํ•ต์‹ฌ ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ) - **Phase 2**: 1์ผ (GPU ๋ Œ๋”๋ง ํ…Œ์ŠคํŠธ) - **Phase 3**: 1์ผ (ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ) **์ด 4-5์ผ** ### **์šฐ์„ ์ˆœ์œ„** 1. **์˜์‚ฌ๊ฒฐ์ •**: Option A vs B ์„ ํƒ 2. **WebMFileReaderTest.cpp** (๊ฐ€์žฅ ํ•ต์‹ฌ์ ) 3. **AV1DecoderTest.cpp** (๋””์ฝ”๋”ฉ ๋กœ์ง) 4. **VideoDecoderFactoryTest.cpp** (ํŒฉํ† ๋ฆฌ ํŒจํ„ด) 5. **SimpleGPURendererTest.cpp** (GPU ์•ˆ์ •์„ฑ) 6. **IntegrationTest.cpp** (์ „์ฒด ๊ฒ€์ฆ) --- ## ๐ŸŽฏ **์„ฑ๊ณต ์ง€ํ‘œ** ### **์ •๋Ÿ‰์  ๋ชฉํ‘œ** - **ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€**: ํ•ต์‹ฌ public ๋ฉ”์„œ๋“œ 80% ์ด์ƒ - **ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ๊ฐ„**: 30์ดˆ ์ด๋‚ด - **ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ˆ˜**: 6๊ฐœ (๊ธฐ์กด 1๊ฐœ + ์‹ ๊ทœ 5๊ฐœ) - **๋นŒ๋“œ ์„ฑ๊ณต๋ฅ **: 100% ### **์ •์„ฑ์  ๋ชฉํ‘œ** - **ํฌ๋ž˜์‹œ ๋ฐฉ์ง€**: ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ํฌ๋ž˜์‹œ ์—†์ด ์™„๋ฃŒ - **ํšŒ๊ท€ ๋ฐฉ์ง€**: ๊ธฐ์กด ๊ธฐ๋Šฅ ๋ณ€๊ฒฝ ์‹œ ํ…Œ์ŠคํŠธ๋กœ ๊ฒ€์ฆ ๊ฐ€๋Šฅ - **์œ ์ง€๋ณด์ˆ˜์„ฑ**: ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€ ์‹œ ํ…Œ์ŠคํŠธ ํŒจํ„ด ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ --- ## โš ๏ธ **์ฃผ์˜์‚ฌํ•ญ** ### **1. ํ™˜๊ฒฝ ์˜์กด์„ฑ ์ตœ์†Œํ™”** - **D3D12**: GPU ์—†๋Š” ํ™˜๊ฒฝ์—์„œ๋„ ์‹คํŒจํ•˜์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌ - **dav1d**: ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋กœ๋“œ ์‹คํŒจ ์‹œ์—๋„ ํ…Œ์ŠคํŠธ ์™„๋ฃŒ - **MediaFoundation**: ์ง€์›๋˜์ง€ ์•Š๋Š” ํ™˜๊ฒฝ์—์„œ graceful failure ### **2. ํ…Œ์ŠคํŠธ ๊ฒฉ๋ฆฌ** - **ํŒŒ์ผ ์‹œ์Šคํ…œ**: ์‹ค์ œ ํŒŒ์ผ์— ์˜์กดํ•˜์ง€ ์•Š๋Š” ํ…Œ์ŠคํŠธ ์šฐ์„  - **์ „์—ญ ์ƒํƒœ**: ํ…Œ์ŠคํŠธ ๊ฐ„ ์ƒํ˜ธ ์˜ํ–ฅ ์ตœ์†Œํ™” - **๋ฉ”๋ชจ๋ฆฌ**: ๊ฐ ํ…Œ์ŠคํŠธ ํ›„ ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ ### **3. ์‹ค์šฉ์  ์ ‘๊ทผ** - **์™„๋ฒฝํ•œ Coverage๋ณด๋‹ค ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์•ˆ์ •์„ฑ** - **๋ณต์žกํ•œ Mock๋ณด๋‹ค ๊ฐ„๋‹จํ•œ ์‹ค์ œ ํ…Œ์ŠคํŠธ** - **์™ธ๋ถ€ ์˜์กด์„ฑ Mock๋ณด๋‹ค ์—๋Ÿฌ ์ผ€์ด์Šค ํ™œ์šฉ** --- **๐ŸŽฏ ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€: "๋‹จ์ˆœํ•˜๊ณ  ์‹ค์šฉ์ ์ธ ํ…Œ์ŠคํŠธ๋กœ ์•ˆ์ •์„ฑ ํ™•๋ณด"** *๋ฌธ์„œ ์ž‘์„ฑ์ผ: 2025-09-23* *์ด์ „ ๋ฌธ์„œ ๋Œ€์ฒด: UNIT_TEST_REFACTORING_PLAN.md (๊ตฌ์‹)*