Skip to content

Commit 82d8718

Browse files
committed
配置传递重构 + 集成测试
- 移除 PackerPolicyItem 抽象方法,改为 PackerPolicy switch 类型派发 - 各策略 CreateProviders 采用独立签名,按需接收参数 - DirectPolicy 现在读取 globalConfig.Base.TargetLanguages(不再硬编码 zh_cn) - IndirectPolicy 接收完整 Config 传递给目标,目标自行合并局域配置 - CompositionPolicy 新增 destType: "lang" 支持 - 新增 CompositionLangFile,与 CompositionJsonFile 对称 - 文件容斥顺序修正:InclusionDomains 优先于 ExclusionDomains - 新增 5 个集成测试,使用 projects/packer-example 真实数据
1 parent c5ef47e commit 82d8718

28 files changed

Lines changed: 731 additions & 281 deletions

src/Packer.Core.Tests/ConfigurationTests/DeserializationTests.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using Packer.Core.Model;
2-
using Packer.Core.Model.Configuration;
1+
using Packer.Core.Model.Configuration;
2+
using Packer.Core.Model.PackerPolicys;
33
using Packer.Core.Model.ResourceFile;
44
using System.Text.Json;
55

@@ -8,6 +8,11 @@ namespace Packer.Core.Tests.ConfigurationTests;
88
/// <summary>测试 1/2/3:配置与策略的解析与组合行为</summary>
99
public class DeserializationTests
1010
{
11+
static readonly FloatingConfig EmptyConfig = new([], [], [], [],
12+
new Dictionary<string, string>(), new Dictionary<string, string>());
13+
static readonly Config EmptyGlobal = new(
14+
new BaseConfig("", ["zh_cn"], "", [], "", [], [], []), EmptyConfig);
15+
1116
static readonly JsonSerializerOptions Opts = new()
1217
{
1318
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
@@ -140,7 +145,7 @@ public void GlobalFilter_InclusionDomain_ForceIncludes()
140145
DestinationReplacement: new Dictionary<string, string>());
141146

142147
// DirectPolicy 默认共享
143-
var providers = new DirectPolicy().CreateProviders(ns, config).ToList();
148+
var providers = new DirectPolicy().CreateProviders(ns, EmptyGlobal, config).ToList();
144149

145150
Assert.Contains(providers, p => p.Destination.EndsWith("font/extra.txt"));
146151
Assert.DoesNotContain(providers, p => p.Destination.EndsWith("sounds/ambient.ogg"));
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
using Packer.Core.Model;
2+
using Packer.Core.Model.Abstract;
3+
using Packer.Core.Model.Configuration;
4+
using Packer.Core.Model.PackerPolicys;
5+
6+
namespace Packer.Core.Tests.IntegrationTests;
7+
8+
/// <summary>
9+
/// 使用 projects/packer-example 的数据做真实集成测试。
10+
/// 在临时目录重建项目结构(CWD 已切换到临时项目根),跑完整打包流程。
11+
/// </summary>
12+
public class PackerExampleTests : IDisposable
13+
{
14+
private readonly string _originalDir = Environment.CurrentDirectory;
15+
private readonly TempDir _tmp = new();
16+
17+
public PackerExampleTests()
18+
{
19+
Environment.CurrentDirectory = _tmp.Path;
20+
}
21+
22+
public void Dispose()
23+
{
24+
Environment.CurrentDirectory = _originalDir;
25+
_tmp.Dispose();
26+
}
27+
28+
// 从 AppContext.BaseDirectory(bin/Debug/net10.0/)回溯到仓库根目录
29+
static readonly string _exampleRoot = Path.GetFullPath(
30+
Path.Combine(AppContext.BaseDirectory,
31+
"..", "..", "..", "..", "..",
32+
"projects", "packer-example", "assets"));
33+
34+
[Fact]
35+
public void DirectPolicy_IncludesZhCn_ExcludesEnUs()
36+
{
37+
CreateProject("example-direct");
38+
var config = LoadTestConfig();
39+
var ns = GetNamespace("example-direct", "direct");
40+
41+
var providers = ns.PackerPolicies
42+
.CreateProviders(ns, config)
43+
.ToList();
44+
45+
Assert.Contains(providers, p => p.Destination.EndsWith("direct/lang/zh_cn.json"));
46+
Assert.DoesNotContain(providers, p => p.Destination.EndsWith("direct/lang/en_us.json"));
47+
Assert.DoesNotContain(providers, p => p.Destination.EndsWith("direct/other.txt"));
48+
}
49+
50+
[Fact]
51+
public void IndirectPolicy_RewritesNamespace()
52+
{
53+
CreateProject("example-direct", "example-indirect");
54+
var config = LoadTestConfig();
55+
var ns = GetNamespace("example-indirect", "indirect");
56+
57+
var providers = ns.PackerPolicies
58+
.CreateProviders(ns, config)
59+
.ToList();
60+
61+
Assert.NotEmpty(providers);
62+
foreach (var p in providers)
63+
Assert.Contains("indirect", p.Destination);
64+
Assert.Contains(providers, p => p.Destination.EndsWith("lang/zh_cn.json"));
65+
}
66+
67+
[Fact]
68+
public void CompositePolicy_MergesMultipleSources()
69+
{
70+
CreateProject("example-direct", "example-indirect", "example-composition", "example-composite");
71+
var config = LoadTestConfig();
72+
var ns = GetNamespace("example-composite", "composite");
73+
74+
var providers = ns.PackerPolicies
75+
.CreateProviders(ns, config)
76+
.ToList();
77+
78+
var zhCn = providers.Where(p => p.Destination.Contains("composite") && p.Destination.EndsWith("lang/zh_cn.json"))
79+
.Cast<KVPFile>().ToList();
80+
Assert.Equal(2, zhCn.Count);
81+
82+
// 第一个来自 composite 自身(Direct),key3 被覆盖为新值
83+
// 第二个来自 indirect(→direct),包含 direct 的全部 18 个 key
84+
var compositeEntries = zhCn[0].Entries;
85+
var directEntries = zhCn[1].Entries;
86+
Assert.Equal("new value3", compositeEntries["key3"]);
87+
Assert.Equal("value1", directEntries["key1"]);
88+
}
89+
90+
[Fact]
91+
public void CompositionPolicy_ExpandsCartesianProduct()
92+
{
93+
CreateProject("example-composition");
94+
var config = LoadTestConfig();
95+
var ns = GetNamespace("example-composition", "composition");
96+
97+
var providers = ns.PackerPolicies
98+
.CreateProviders(ns, config)
99+
.ToList();
100+
101+
Assert.Single(providers);
102+
var provider = providers[0] as KVPFile;
103+
Assert.NotNull(provider);
104+
105+
Assert.Equal(200, provider.Entries.Count);
106+
Assert.Equal("value_0_00", provider.Entries["key_0_00"]);
107+
Assert.Equal("altvalue_9_09", provider.Entries["altkey_9_09"]);
108+
}
109+
110+
[Fact]
111+
public void FullPipeline_ProducesAllExpectedDestinations()
112+
{
113+
CreateProject("example-direct", "example-indirect", "example-composite", "example-composition");
114+
var config = LoadTestConfig();
115+
116+
var mods = new[] { "example-direct", "example-indirect", "example-composite", "example-composition" };
117+
var allNamespaces = mods.Select(m => GetNamespace(m, m.Split('-').Last()));
118+
119+
var allProviders = allNamespaces
120+
.SelectMany(ns => ns.PackerPolicies.CreateProviders(ns, config))
121+
.ToLookup(p => p.Destination);
122+
123+
var dests = allProviders.Select(g => g.Key).ToList();
124+
Assert.Contains(dests, d => d.Contains("direct/lang/zh_cn.json"));
125+
Assert.Contains(dests, d => d.Contains("indirect/lang/zh_cn.json"));
126+
Assert.Contains(dests, d => d.Contains("composite/lang/zh_cn.json"));
127+
Assert.Contains(dests, d => d.Contains("composition/lang/zh_cn.json"));
128+
}
129+
130+
// ── 辅助方法 ──
131+
132+
private void CreateProject(params string[] modNames)
133+
{
134+
foreach (var mod in modNames)
135+
{
136+
var src = Path.Combine(_exampleRoot, mod);
137+
foreach (var nsDir in Directory.EnumerateDirectories(src))
138+
{
139+
var ns = Path.GetFileName(nsDir);
140+
var dest = Path.Combine(_tmp.Path, "projects", "assets", mod, "packer-example", ns);
141+
CopyDirectory(nsDir, dest);
142+
}
143+
}
144+
}
145+
146+
private static Config LoadTestConfig()
147+
{
148+
return new Config(
149+
new BaseConfig(
150+
Version: "packer-example",
151+
TargetLanguages: ["zh_cn"],
152+
McMetaTemplate: "",
153+
McMetaParameters: [],
154+
ReadmeTemplate: "",
155+
ReadmeParameters: [],
156+
ExclusionMods: [],
157+
ExclusionNamespaces: []
158+
),
159+
new FloatingConfig(
160+
InclusionDomains: [],
161+
ExclusionDomains: [],
162+
ExclusionPaths: ["local-config.json", "packer-policy.json"],
163+
InclusionPaths: [],
164+
CharacterReplacement: new Dictionary<string, string>
165+
{
166+
["REPLACEMENT"] = "被替换的内容"
167+
},
168+
DestinationReplacement: new Dictionary<string, string>()
169+
)
170+
);
171+
}
172+
173+
private INamespaceResource GetNamespace(string modName, string nsName)
174+
{
175+
var nsDir = Path.Combine(_tmp.Path, "projects", "assets", modName, "packer-example", nsName);
176+
return new AssetsNamespaceResource(nsDir, nsName, modName, "packer-example");
177+
}
178+
179+
private static void CopyDirectory(string src, string dest)
180+
{
181+
Directory.CreateDirectory(dest);
182+
foreach (var file in Directory.EnumerateFiles(src, "*", SearchOption.AllDirectories))
183+
{
184+
var relative = Path.GetRelativePath(src, file);
185+
var target = Path.Combine(dest, relative);
186+
Directory.CreateDirectory(Path.GetDirectoryName(target)!);
187+
File.Copy(file, target);
188+
}
189+
}
190+
}

src/Packer.Core.Tests/PolicyTests/PolicyTypeTests.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Packer.Core.Model;
22
using Packer.Core.Model.Configuration;
3+
using Packer.Core.Model.PackerPolicys;
34
using Packer.Core.Model.ResourceFile;
45

56
namespace Packer.Core.Tests.PolicyTests;
@@ -10,6 +11,9 @@ public class PolicyTypeTests
1011
static readonly FloatingConfig EmptyConfig = new([], [], [], [],
1112
new Dictionary<string, string>(), new Dictionary<string, string>());
1213

14+
static readonly Config EmptyGlobal = new(
15+
new BaseConfig("", ["zh_cn"], "", [], "", [], [], []), EmptyConfig);
16+
1317
// ── 测试 4:默认打包策略(Direct)与浮动配置组合时过滤正确 ──
1418

1519
[Fact]
@@ -24,7 +28,7 @@ public void DirectPolicy_WithFilters_ExcludesCorrectly()
2428
var config = new FloatingConfig([], [], [], ["packer-policy.json", "local-config.json"],
2529
new Dictionary<string, string>(), new Dictionary<string, string>());
2630

27-
var providers = new DirectPolicy().CreateProviders(ns, config).ToList();
31+
var providers = new DirectPolicy().CreateProviders(ns, EmptyGlobal, config).ToList();
2832
Assert.Contains(providers, p => p.Destination.Contains("zh_cn"));
2933
Assert.DoesNotContain(providers, p => p.Destination.Contains("en_us"));
3034
}
@@ -45,7 +49,7 @@ public void MultiplePolicies_ExecuteAll()
4549
System.IO.Path.Combine(dir.Path, "lang/comp.json"), "json"));
4650

4751
var ns = new AssetsNamespaceResource(dir.Path, "ns", "mod", "1.21");
48-
var providers = policyList.CreateProviders(ns, EmptyConfig).ToList();
52+
var providers = policyList.CreateProviders(ns, EmptyGlobal).ToList();
4953

5054
Assert.Equal(2, providers.Count);
5155
Assert.IsType<JsonFile>(providers[0]);
@@ -65,7 +69,7 @@ public void DirectPolicy_Produces_ExpectedProviders()
6569
var config = new FloatingConfig([], [], [], ["packer-policy.json", "local-config.json"],
6670
new Dictionary<string, string>(), new Dictionary<string, string>());
6771

68-
var providers = new DirectPolicy().CreateProviders(ns, config).ToList();
72+
var providers = new DirectPolicy().CreateProviders(ns, EmptyGlobal, config).ToList();
6973

7074
Assert.Contains(providers, p => p is JsonFile);
7175
Assert.Contains(providers, p => p is LangFile);
@@ -87,7 +91,7 @@ public void IndirectPolicy_Recursion_Works()
8791
var config = new FloatingConfig([], [], [], ["packer-policy.json", "local-config.json"],
8892
new Dictionary<string, string>(), new Dictionary<string, string>());
8993

90-
var providers = ns.PackerPolicies.CreateProviders(ns, config).ToList();
94+
var providers = ns.PackerPolicies.CreateProviders(ns, EmptyGlobal).ToList();
9195

9296
var dests = providers.Select(p => p.Destination).ToList();
9397
Assert.Contains(dests, d => d.Contains("srcns"));

src/Packer.Core.Tests/ProviderTests/NamespaceProviderTests.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
using Packer.Core.Model;
2-
using Packer.Core.Model.Abstract;
1+
using Packer.Core.Model.Abstract;
32
using Packer.Core.Model.Configuration;
43
using Packer.Core.Model.ModProvider;
4+
using Packer.Core.Model.PackerPolicys;
55

66
namespace Packer.Core.Tests.ProviderTests;
77

88
/// <summary>测试 9/13:命名空间提供程序和命名空间资源</summary>
99
public class NamespaceProviderTests
1010
{
11+
static readonly FloatingConfig EmptyFloating = new([], [], [], [],
12+
new Dictionary<string, string>(), new Dictionary<string, string>());
13+
static readonly Config EmptyGlobal = new(
14+
new BaseConfig("", ["zh_cn"], "", [], "", [], [], []), EmptyFloating);
1115
// ── 测试 13:错误参数的命名空间类 ──
1216

1317
[Fact]
@@ -49,7 +53,7 @@ public void EmptyNamespace_Directory_Returns_NoFileProviders()
4953
using var dir = new TempDir();
5054
var ns = new AssetsNamespaceResource(dir.Path, "ns", "mod", "1.21");
5155

52-
var providers = ns.PackerPolicies.CreateProviders(ns, FloatingConfig.Shared).ToList();
56+
var providers = ns.PackerPolicies.CreateProviders(ns, EmptyGlobal).ToList();
5357
Assert.Empty(providers);
5458
}
5559
}

src/Packer.Core.Tests/ResolverTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Packer.Core.Model.PackerPolicys;
12
using System.Text.Json;
23

34
namespace Packer.Core.Tests;

src/Packer.Core/Model/Configuration/BaseConfig.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using Packer.Core.Model.Abstract;
2-
using Packer.Core.Model.ResourceFile;
3-
4-
namespace Packer.Core.Model.Configuration;
1+
namespace Packer.Core.Model.Configuration;
52

63
/// <summary>
74
/// 基础配置,版本唯一

src/Packer.Core/Model/Configuration/FloatingConfig.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using Packer.Core.Model.Abstract;
2-
using System.Collections.Frozen;
3-
4-
namespace Packer.Core.Model.Configuration;
1+
namespace Packer.Core.Model.Configuration;
52

63
/// <summary>
74
/// 浮动配置,可与命名空间下的文件合并
@@ -60,7 +57,10 @@ public static FloatingConfig Load(string directoryPath)
6057
/// <summary>
6158
/// 从命名空间资源加载局域配置。
6259
/// </summary>
63-
public static FloatingConfig Load(INamespaceResource ns) => Load(ns.LocalPath);
60+
public static FloatingConfig Load(INamespaceResource ns)
61+
{
62+
return Load(ns.LocalPath);
63+
}
6464

6565
/// <summary>
6666
/// 从另一对象合并配置

src/Packer.Core/Model/ModProvider/AssetsModProvider.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using Packer.Core.Model.Abstract;
2-
3-
namespace Packer.Core.Model.ModProvider;
1+
namespace Packer.Core.Model.ModProvider;
42

53
/// <summary>
64
/// 基于 <c>./projects/assets/{modName}/{version}/{namespace}/</c> 目录结构的模组提供者。

src/Packer.Core/Model/ModProvider/AssetsNamespaceResource.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using Packer.Core.Model.Abstract;
2-
using Packer.Core.Model.Configuration;
3-
4-
namespace Packer.Core.Model.ModProvider;
1+
namespace Packer.Core.Model.ModProvider;
52

63
/// <summary>
74
/// 基于 <c>./projects/assets</c> 目录的命名空间资源。

src/Packer.Core/Model/ModProvider/GitChangedModProvider.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using LibGit2Sharp;
2-
3-
namespace Packer.Core.Model.ModProvider;
1+
namespace Packer.Core.Model.ModProvider;
42

53
/// <summary>
64
/// 基于 git diff 的增量命名空间提供器。

0 commit comments

Comments
 (0)