feat: 순수 .NET Provider 3종 (Data/Vector/ImageOptim) + Loss 분류 정확화
검증 가능한 순수 .NET 변환기를 우선 추가. 외부 도구 불필요, 실제 동작까지 테스트로 검증. - DataProvider: csv<->json, csv<->xlsx 원자 엣지 (json<->xlsx는 그래프가 csv 경유 자동 합성). CsvHelper/ClosedXML - VectorProvider: svg->png/pdf (svg->기타이미지는 png 경유 합성). Svg.Skia - ImageOptimProvider: 동일포맷 재압축 self-edge (jpg/png/webp/tiff/bmp) - Loss 분류 정확화: OCR·문서/HTML->이미지=Rasterize, ->pdf=Recode (멀티홉 이상경로 svg->pdf->docx->jpg 차단) - 그래프 합성/실동작 테스트 9개 추가 (총 23개 통과) - DocumentFormat.OpenXml 3.1.0->최신 (ClosedXML 호환)
This commit is contained in:
parent
e3ef59e009
commit
d49ee6365f
13 changed files with 655 additions and 11 deletions
68
src/Everything2Everything.Tests/DataProviderTests.cs
Normal file
68
src/Everything2Everything.Tests/DataProviderTests.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Everything2Everything.Core;
|
||||
using Everything2Everything.Core.Converters;
|
||||
using Everything2Everything.Core.Providers;
|
||||
using Xunit;
|
||||
|
||||
namespace Everything2Everything.Tests;
|
||||
|
||||
public class DataProviderTests
|
||||
{
|
||||
private static string TempDir()
|
||||
{
|
||||
var d = Path.Combine(Path.GetTempPath(), "e2e_test_" + System.Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(d);
|
||||
return d;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Csv_To_Json_To_Csv_Roundtrip()
|
||||
{
|
||||
var dir = TempDir();
|
||||
var csv = Path.Combine(dir, "t.csv");
|
||||
await File.WriteAllTextAsync(csv, "name,age\nAlice,30\nBob,25\n");
|
||||
|
||||
var p = new DataProvider();
|
||||
var r1 = await p.ConvertAsync(csv, dir, ".json", new ConvertOptions(), null, CancellationToken.None);
|
||||
Assert.Equal(ConvertStatus.Success, r1.Status);
|
||||
var json = await File.ReadAllTextAsync(r1.OutputPaths[0]);
|
||||
Assert.Contains("Alice", json);
|
||||
Assert.Contains("30", json);
|
||||
|
||||
var r2 = await p.ConvertAsync(r1.OutputPaths[0], dir, ".csv", new ConvertOptions(), null, CancellationToken.None);
|
||||
Assert.Equal(ConvertStatus.Success, r2.Status);
|
||||
var csv2 = await File.ReadAllTextAsync(r2.OutputPaths[0]);
|
||||
Assert.Contains("Alice", csv2);
|
||||
Assert.Contains("age", csv2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Csv_To_Xlsx_To_Csv_Roundtrip()
|
||||
{
|
||||
var dir = TempDir();
|
||||
var csv = Path.Combine(dir, "t.csv");
|
||||
await File.WriteAllTextAsync(csv, "a,b\n1,x\n2,y\n");
|
||||
|
||||
var p = new DataProvider();
|
||||
var r1 = await p.ConvertAsync(csv, dir, ".xlsx", new ConvertOptions(), null, CancellationToken.None);
|
||||
Assert.Equal(ConvertStatus.Success, r1.Status);
|
||||
Assert.True(new FileInfo(r1.OutputPaths[0]).Length > 0);
|
||||
|
||||
var r2 = await p.ConvertAsync(r1.OutputPaths[0], dir, ".csv", new ConvertOptions(), null, CancellationToken.None);
|
||||
Assert.Equal(ConvertStatus.Success, r2.Status);
|
||||
var csv2 = await File.ReadAllTextAsync(r2.OutputPaths[0]);
|
||||
Assert.Contains("a", csv2);
|
||||
Assert.Contains("x", csv2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultGraph_JsonToXlsx_ComposedViaCsv()
|
||||
{
|
||||
var graph = Everything2EverythingBootstrap.CreateDefault().Providers.Graph;
|
||||
var path = graph.FindBestPath(".json", ".xlsx", maxHops: 3);
|
||||
Assert.NotNull(path);
|
||||
Assert.Equal(2, path!.Count); // json→csv→xlsx 자동 합성
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="14.13.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
|
|
|
|||
57
src/Everything2Everything.Tests/ImageOptimProviderTests.cs
Normal file
57
src/Everything2Everything.Tests/ImageOptimProviderTests.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Everything2Everything.Core;
|
||||
using Everything2Everything.Core.Converters;
|
||||
using Everything2Everything.Core.Providers;
|
||||
using ImageMagick;
|
||||
using Xunit;
|
||||
|
||||
namespace Everything2Everything.Tests;
|
||||
|
||||
public class ImageOptimProviderTests
|
||||
{
|
||||
private static string TempDir()
|
||||
{
|
||||
var d = Path.Combine(Path.GetTempPath(), "e2e_test_" + System.Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(d);
|
||||
return d;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Png_Optimize_ProducesOutput()
|
||||
{
|
||||
var dir = TempDir();
|
||||
var png = Path.Combine(dir, "t.png");
|
||||
using (var img = new MagickImage(MagickColors.Red, 64, 64)) img.Write(png);
|
||||
|
||||
var p = new ImageOptimProvider();
|
||||
var r = await p.ConvertAsync(png, dir, ".png", new ConvertOptions(), null, CancellationToken.None);
|
||||
Assert.Equal(ConvertStatus.Success, r.Status);
|
||||
Assert.True(File.Exists(r.OutputPaths[0]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Jpg_Optimize_LowerQuality_ProducesOutput()
|
||||
{
|
||||
var dir = TempDir();
|
||||
var jpg = Path.Combine(dir, "t.jpg");
|
||||
using (var img = new MagickImage(MagickColors.SteelBlue, 256, 256)) img.Write(jpg);
|
||||
|
||||
var p = new ImageOptimProvider();
|
||||
var options = new ConvertOptions();
|
||||
options.Jpeg.Quality = 30;
|
||||
var r = await p.ConvertAsync(jpg, dir, ".jpg", options, null, CancellationToken.None);
|
||||
Assert.Equal(ConvertStatus.Success, r.Status);
|
||||
Assert.True(File.Exists(r.OutputPaths[0]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultGraph_PngSelfEdge_ExistsForOptimization()
|
||||
{
|
||||
var graph = Everything2EverythingBootstrap.CreateDefault().Providers.Graph;
|
||||
// 이미지 최적화 self-edge가 그래프에 등록되어 png→png가 가능 (압축 의도)
|
||||
Assert.NotNull(graph.FindBestPath(".png", ".png"));
|
||||
Assert.NotNull(graph.FindBestPath(".jpg", ".jpg"));
|
||||
}
|
||||
}
|
||||
58
src/Everything2Everything.Tests/VectorProviderTests.cs
Normal file
58
src/Everything2Everything.Tests/VectorProviderTests.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Everything2Everything.Core;
|
||||
using Everything2Everything.Core.Converters;
|
||||
using Everything2Everything.Core.Providers;
|
||||
using Xunit;
|
||||
|
||||
namespace Everything2Everything.Tests;
|
||||
|
||||
public class VectorProviderTests
|
||||
{
|
||||
private const string Svg =
|
||||
"<svg xmlns='http://www.w3.org/2000/svg' width='100' height='80'><rect width='100' height='80' fill='red'/><circle cx='50' cy='40' r='20' fill='blue'/></svg>";
|
||||
|
||||
private static string TempDir()
|
||||
{
|
||||
var d = Path.Combine(Path.GetTempPath(), "e2e_test_" + System.Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(d);
|
||||
return d;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Svg_To_Png_ProducesValidFile()
|
||||
{
|
||||
var dir = TempDir();
|
||||
var svg = Path.Combine(dir, "t.svg");
|
||||
await File.WriteAllTextAsync(svg, Svg);
|
||||
|
||||
var p = new VectorProvider();
|
||||
var r = await p.ConvertAsync(svg, dir, ".png", new ConvertOptions(), null, CancellationToken.None);
|
||||
Assert.Equal(ConvertStatus.Success, r.Status);
|
||||
Assert.True(new FileInfo(r.OutputPaths[0]).Length > 100);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Svg_To_Pdf_ProducesValidFile()
|
||||
{
|
||||
var dir = TempDir();
|
||||
var svg = Path.Combine(dir, "t.svg");
|
||||
await File.WriteAllTextAsync(svg, Svg);
|
||||
|
||||
var p = new VectorProvider();
|
||||
var r = await p.ConvertAsync(svg, dir, ".pdf", new ConvertOptions(), null, CancellationToken.None);
|
||||
Assert.Equal(ConvertStatus.Success, r.Status);
|
||||
Assert.True(new FileInfo(r.OutputPaths[0]).Length > 100);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultGraph_SvgToJpg_ComposedViaPng()
|
||||
{
|
||||
var graph = Everything2EverythingBootstrap.CreateDefault().Providers.Graph;
|
||||
var path = graph.FindBestPath(".svg", ".jpg", maxHops: 3);
|
||||
Assert.NotNull(path); // svg→png→jpg 자동 합성
|
||||
var route = string.Join(" -> ", path!.Select(e => e.From)) + " -> " + path[^1].To;
|
||||
Assert.True(path.Count == 2, "예상 svg→png→jpg(2홉), 실제: " + route);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue