From e95ec967e6294153f7f874d94ddb90392594c28e Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Tue, 2 Jun 2026 09:53:26 +0900 Subject: [PATCH] =?UTF-8?q?refactor(P5b-2):=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EA=B5=90=EC=A7=91=ED=95=A9=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=9D=84=20ProviderRegistry=EB=A1=9C=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C=20(=EB=A7=A4=ED=8A=B8=EB=A6=AD=EC=8A=A4=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=ED=99=94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MainWindow.RefreshAvailableOutputFormats에 박혀 있던 '큐 전체 공통 출력' 교집합 계산을 ProviderRegistry.AvailableOutputsForFiles 순수 쿼리로 추출 — 코드비하인드 18줄 → 2줄, 헤드리스 테스트 가능. - ProviderRegistry.AvailableOutputsForFiles(sourcePaths): 비면 전체 출력, 아니면 OutputsForFile 교집합. - MainWindow: 인라인 교집합 루프 제거 → 레지스트리 메서드 호출. UI(콤보 빌드)는 그대로. - 테스트 2개: 빈 입력=전체, png+csv 혼합 시 jpg가 교집합에서 빠짐(매트릭스 필터 핵심 동작). 81 → 83개 테스트 전부 그린, 빌드 0경고/0오류. --- .../Views/MainWindow.xaml.cs | 21 ++---------------- .../Providers/ProviderRegistry.cs | 19 ++++++++++++++++ .../ConversionGraphTests.cs | 22 +++++++++++++++++++ 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/Everything2Everything.App/Views/MainWindow.xaml.cs b/src/Everything2Everything.App/Views/MainWindow.xaml.cs index acb047c..c4da158 100644 --- a/src/Everything2Everything.App/Views/MainWindow.xaml.cs +++ b/src/Everything2Everything.App/Views/MainWindow.xaml.cs @@ -952,25 +952,8 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow { if (OutputFormatCombo is null) return; - var engine = _engine; - IReadOnlyCollection available; - - if (_activeQueue.Count == 0) - { - available = engine.Providers.AllOutputExtensions; - } - else - { - HashSet? intersection = null; - foreach (var item in _activeQueue) - { - var outs = engine.Providers.OutputsForFile(item.SourcePath); - var outSet = new HashSet(outs, StringComparer.OrdinalIgnoreCase); - if (intersection is null) intersection = outSet; - else intersection.IntersectWith(outSet); - } - available = intersection ?? new HashSet(StringComparer.OrdinalIgnoreCase); - } + var available = _engine.Providers.AvailableOutputsForFiles( + _activeQueue.Select(q => q.SourcePath).ToList()); var visible = AllFormats.Where(f => available.Contains(f.Extension)).ToList(); if (visible.Count == 0) diff --git a/src/Everything2Everything.Core/Providers/ProviderRegistry.cs b/src/Everything2Everything.Core/Providers/ProviderRegistry.cs index e3a6ce1..a1c8507 100644 --- a/src/Everything2Everything.Core/Providers/ProviderRegistry.cs +++ b/src/Everything2Everything.Core/Providers/ProviderRegistry.cs @@ -79,6 +79,25 @@ public sealed class ProviderRegistry public IReadOnlyList OutputsForFile(string sourcePath) => OutputsForInput(Path.GetExtension(sourcePath)); + /// + /// 여러 입력 파일이 '공통으로' 변환 가능한 출력 확장자(교집합). UI 매트릭스 자동 필터링의 핵심 — + /// 큐의 모든 파일을 같은 형식으로 변환할 수 있는 출력만 노출한다. 비어 있으면 전체 출력 확장자. + /// (MainWindow.RefreshAvailableOutputFormats에서 추출 — 순수 쿼리라 헤드리스 테스트 가능.) + /// + public IReadOnlyCollection AvailableOutputsForFiles(IReadOnlyCollection sourcePaths) + { + if (sourcePaths.Count == 0) return AllOutputExtensions; + + HashSet? intersection = null; + foreach (var path in sourcePaths) + { + var outs = new HashSet(OutputsForFile(path), StringComparer.OrdinalIgnoreCase); + if (intersection is null) intersection = outs; + else intersection.IntersectWith(outs); + } + return intersection ?? new HashSet(StringComparer.OrdinalIgnoreCase); + } + public IReadOnlyCollection AllInputExtensions => _allInputs; public IReadOnlyCollection AllOutputExtensions => _allOutputs; diff --git a/src/Everything2Everything.Tests/ConversionGraphTests.cs b/src/Everything2Everything.Tests/ConversionGraphTests.cs index 1a186a0..f695801 100644 --- a/src/Everything2Everything.Tests/ConversionGraphTests.cs +++ b/src/Everything2Everything.Tests/ConversionGraphTests.cs @@ -223,4 +223,26 @@ public class RegistryGraphIntegrationTests Assert.Contains(".jpg", svgOut); // 멀티홉 svg→png→jpg Assert.Contains(".pdf", svgOut); // 직접 } + + [Fact] + public void AvailableOutputsForFiles_EmptyReturnsAllOutputs() + { + var reg = Everything2EverythingBootstrap.CreateDefault().Providers; + var all = reg.AvailableOutputsForFiles(System.Array.Empty()); + Assert.Equal(reg.AllOutputExtensions.Count, all.Count); + } + + [Fact] + public void AvailableOutputsForFiles_IntersectsAcrossInputs() + { + var reg = Everything2EverythingBootstrap.CreateDefault().Providers; + + // png 단독: jpg 등 이미지 출력 가능 + var pngOnly = reg.AvailableOutputsForFiles(new[] { "a.png" }); + Assert.Contains(".jpg", pngOnly); + + // png + csv 혼합: csv는 jpg로 못 가므로 교집합에서 jpg가 빠진다(UI 매트릭스 자동 필터 핵심). + var mixed = reg.AvailableOutputsForFiles(new[] { "a.png", "b.csv" }); + Assert.DoesNotContain(".jpg", mixed); + } }