Skip to content

Commit 411316a

Browse files
authored
Merge pull request #76 from sillsdev/allow-in-file-date-format-specifiers
Added support for date insertion in localized files (#76)
2 parents cd70c12 + 5c10f45 commit 411316a

File tree

5 files changed

+306
-9
lines changed

5 files changed

+306
-9
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1616

1717
## [Unreleased]
1818

19+
### Added
20+
21+
- [SIL.BuildTasks] Added FileUpdate.FileLocalePattern (optional param) to infer a locale (e.g., for a localized release notes file) to use when doing date insertion involving month names or abbreviations.
22+
1923
### Changed
24+
2025
- [SIL.BuildTasks.AWS] Changed case of text in log message from "Publishing Sourcefiles" "Publishing SourceFiles". If anything is doing a case-sensitive parse of the log file, looking for this text, this could be a breaking change.
26+
- [SIL.BuildTasks] Changed FileUpdate.DatePlaceholder to allow the caller to specify a special placeholder `_DATE(*)_` that will look not only for `_DATE_` but also variants that include a date format specifier, such as `_DATE(MMM d, yyyy)_` or `_DATE(MM/yyyy)_` and will use the date format specified instead of the DateFormat.
2127

2228
### Deprecated
2329

SIL.BuildTasks.Tests/FileUpdateTests.cs

Lines changed: 201 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
// Copyright (c) 2024 SIL Global
1+
// Copyright (c) 2025 SIL Global
22
// This software is licensed under the MIT License (http://opensource.org/licenses/MIT)
33

44
using System;
55
using NUnit.Framework;
6+
using SIL.Providers;
7+
using SIL.TestUtilities.Providers;
8+
69
// Sadly, Resharper wants to change Is.EqualTo to NUnit.Framework.Is.EqualTo
710
// ReSharper disable AccessToStaticMemberViaDerivedType
811

@@ -11,6 +14,19 @@ namespace SIL.BuildTasks.Tests
1114
[TestFixture]
1215
public class FileUpdateTests
1316
{
17+
[SetUp]
18+
public void Setup()
19+
{
20+
DateTimeProvider.SetProvider(
21+
new ReproducibleDateTimeProvider(new DateTime(2026, 4, 16)));
22+
}
23+
24+
[TearDown]
25+
public void TearDown()
26+
{
27+
DateTimeProvider.ResetToDefault();
28+
}
29+
1430
[TestCase("This is the story of the frog prince.", "frog", "monkey",
1531
ExpectedResult = "This is the story of the monkey prince.")]
1632
[TestCase("This is the story of the frog prince.", "f[^ ]+g", "toad",
@@ -62,6 +78,106 @@ public void GetModifiedContents_RegexTextNotMatched_Throws(string origContents,
6278
Assert.That(ex.Message, Is.EqualTo($"No replacements made. Regex: '{regex}'; ReplacementText: '{replacement}'"));
6379
}
6480

81+
[TestCase("_DATE_ _VERSION_\r\nStuff", "_DATE_", "M/yyyy", "4/2026 3.2.1\r\nStuff")]
82+
[TestCase("_DATE_ _VERSION_\r\nStuff done before _DATE_", "_DATE_", "M/yyyy", "4/2026 3.2.1\r\nStuff done before 4/2026")]
83+
[TestCase("&DATE; _VERSION_\r\n- point #1", "&DATE;", "dd-MM-yy", "16-04-26 3.2.1\r\n- point #1")]
84+
[TestCase("DATE _VERSION_", "DATE", "dd MMMM, yyyy", "16 April, 2026 3.2.1")]
85+
[TestCase("DATE _VERSION_", "DATE", null, "16/Apr/2026 3.2.1")] // Uses default date format
86+
public void GetModifiedContents_DateLiteral_InsertsDateWithSpecifiedDateFormat(
87+
string origContents, string datePlaceholder, string dateFormat,
88+
string expectedResult)
89+
{
90+
var updater = new FileUpdate
91+
{
92+
Regex = "_VERSION_",
93+
ReplacementText = "3.2.1",
94+
DatePlaceholder = datePlaceholder,
95+
DateFormat = dateFormat
96+
};
97+
98+
var result = updater.GetModifiedContents(origContents);
99+
Assert.That(result, Is.EqualTo(expectedResult));
100+
}
101+
102+
[TestCase("_DATE_ _VERSION_\r\nStuff", "M/yyyy", "4/2026 3.2.1\r\nStuff")]
103+
[TestCase("_DATE_ _VERSION_\r\nStuff done before _DATE_", "dd-MM-yy", "16-04-26 3.2.1\r\nStuff done before 16-04-26")]
104+
public void GetModifiedContents_SpecialDatePlaceholderButFileDoesNotSpecifyFormat_InsertsDateWithSpecifiedDateFormat(
105+
string origContents, string dateFormat, string expectedResult)
106+
{
107+
var updater = new FileUpdate
108+
{
109+
Regex = "_VERSION_",
110+
ReplacementText = "3.2.1",
111+
DatePlaceholder = "_DATE(*)_",
112+
DateFormat = dateFormat
113+
};
114+
115+
var result = updater.GetModifiedContents(origContents);
116+
Assert.That(result, Is.EqualTo(expectedResult));
117+
}
118+
119+
[TestCase("MM-yy")]
120+
[TestCase("dd MMMM")]
121+
public void GetModifiedContents_SpecialDatePlaceholderWithFileSpecifyingFormat_InsertsDateWithFormatFromFile(
122+
string format)
123+
{
124+
var origContents = $"_DATE({format})_\r\nStuff";
125+
126+
var updater = new FileUpdate
127+
{
128+
Regex = "(.*)",
129+
ReplacementText = "$1",
130+
DatePlaceholder = "_DATE(*)_",
131+
};
132+
133+
var currentDate = DateTimeProvider.Current.UtcNow.ToString(format);
134+
135+
var result = updater.GetModifiedContents(origContents);
136+
Assert.That(result, Is.EqualTo($"{currentDate}\r\nStuff"));
137+
}
138+
139+
[TestCase("MM-yyyy", "d MMMM yy")]
140+
[TestCase("dd MMMM", "MM/dd/yyyy")]
141+
public void GetModifiedContents_SpecialDatePlaceholderWithFileSpecifyingMultipleFormats_InsertsDateWithFormatsFromFile(
142+
string format1, string format2)
143+
{
144+
var origContents = $"First _DATE({format1})_\r\nSecond _DATE_\r\nLast _DATE({format2})_";
145+
146+
var updater = new FileUpdate
147+
{
148+
Regex = "(.*)",
149+
ReplacementText = "$1",
150+
DatePlaceholder = "_DATE(*)_",
151+
};
152+
153+
var currentDate = DateTimeProvider.Current.UtcNow;
154+
var currentDate1 = currentDate.ToString(format1);
155+
var currentDate2 = currentDate.ToString(format2);
156+
157+
var result = updater.GetModifiedContents(origContents);
158+
Assert.That(result, Is.EqualTo($"First {currentDate1}\r\nSecond 16/Apr/2026\r\nLast {currentDate2}"));
159+
}
160+
161+
[TestCase("es", "abril")]
162+
[TestCase("fr", "avril")]
163+
public void GetModifiedContents_SpecialDatePlaceholderWithLocalizedFileSpecifyingFormat_InsertsLocaleSpecificDateWithFormatFromFile(
164+
string locale, string localizedMonthName)
165+
{
166+
var origContents = "_DATE(d MMMM yyyy)_\r\nStuff";
167+
168+
var updater = new FileUpdate
169+
{
170+
File = $"ReleaseNotes.{locale}.md",
171+
FileLocalePattern = @"\.(?<locale>[a-z]{2}(-\w+)?)\.md$",
172+
Regex = "(.*)",
173+
ReplacementText = "$1",
174+
DatePlaceholder = "_DATE(*)_",
175+
};
176+
177+
var result = updater.GetModifiedContents(origContents);
178+
Assert.That(result, Is.EqualTo($"16 {localizedMonthName} 2026\r\nStuff"));
179+
}
180+
65181
[Test]
66182
public void GetModifiedContents_InvalidRegex_Throws()
67183
{
@@ -74,5 +190,89 @@ public void GetModifiedContents_InvalidRegex_Throws()
74190
var ex = Assert.Throws<Exception>(() => updater.GetModifiedContents("Whatever"));
75191
Assert.That(ex.Message, Is.EqualTo($"Invalid regular expression: parsing \"{updater.Regex}\" - Not enough )'s."));
76192
}
193+
194+
[Test]
195+
public void FileLocalePattern_InvalidRegex_ThrowsArgumentException()
196+
{
197+
const string expr = @"ReleaseNotes\.(.*\.md";
198+
Assert.That(() =>
199+
{
200+
_ = new FileUpdate
201+
{
202+
FileLocalePattern = expr,
203+
ReplacementText = "oops"
204+
};
205+
}, Throws.ArgumentException.With.Message.EqualTo($"FileLocalePattern: Invalid regular expression: parsing \"{expr}\" - Not enough )'s."));
206+
}
207+
208+
[TestCase("es")]
209+
[TestCase("fr")]
210+
[TestCase("zh-CN")]
211+
public void GetCultureFromFileName_MatchLocaleGroupToKnownCulture_GetsSpecifiedCulture(string localeSpecifier)
212+
{
213+
var fileUpdater = new FileUpdate
214+
{
215+
File = $"ReleaseNotes.{localeSpecifier}.md",
216+
FileLocalePattern = @"\.(?<locale>[a-z]{2}(-\w+)?)\.md$",
217+
};
218+
219+
Assert.That(fileUpdater.GetCultureFromFileName().IetfLanguageTag,
220+
Is.EqualTo(localeSpecifier));
221+
}
222+
223+
[TestCase("zz-Unknown")]
224+
[TestCase("qq-Weird")]
225+
public void GetCultureFromFileName_MatchLocaleGroupToUnknownCulture_ReturnsNull(string localeSpecifier)
226+
{
227+
var fileUpdater = new FileUpdate
228+
{
229+
File = $"ReleaseNotes.{localeSpecifier}.md",
230+
FileLocalePattern = @"\.(?<locale>[a-z]{2}(-\w+)?)\.md$",
231+
};
232+
233+
Assert.That(fileUpdater.GetCultureFromFileName(), Is.Null);
234+
}
235+
236+
[TestCase("es")]
237+
[TestCase("fr-FR")]
238+
[TestCase("de")]
239+
public void GetCultureFromFileName_EntireMatchIsKnownCulture_GetsSpecifiedCulture(string localeSpecifier)
240+
{
241+
var fileUpdater = new FileUpdate
242+
{
243+
File = $"ReleaseNotes.{localeSpecifier}.md",
244+
FileLocalePattern = @"(?<=\.)es|fr-FR|de(?=\.)",
245+
};
246+
247+
Assert.That(fileUpdater.GetCultureFromFileName().IetfLanguageTag,
248+
Is.EqualTo(localeSpecifier));
249+
}
250+
251+
[TestCase("My.bat.ate.your.homework.md", @"(?<=\.)[a-z]{4}(?=\.)")]
252+
[TestCase("ReleaseNotes.htm", ".+")]
253+
public void GetCultureFromFileName_EntireMatchIsUnknownCulture_ReturnsNull(string fileName, string pattern)
254+
{
255+
var fileUpdater = new FileUpdate
256+
{
257+
File = fileName,
258+
FileLocalePattern = pattern,
259+
};
260+
261+
Assert.That(fileUpdater.GetCultureFromFileName(), Is.Null);
262+
}
263+
264+
[TestCase("My.bat.ate.your.homework.md", @"(?<=\.)[a-z]{22}(?=\.)")]
265+
[TestCase("ReleaseNotes.htm", @"(?<=\.)es|fr-FR|de(?=\.)")]
266+
[TestCase("ReleaseNotes.htm", @"\.(?<locale>[a-z]{2}(-\w+)?)\.md$")]
267+
public void GetCultureFromFileName_NoMatch_ReturnsNull(string fileName, string pattern)
268+
{
269+
var fileUpdater = new FileUpdate
270+
{
271+
File = fileName,
272+
FileLocalePattern = pattern,
273+
};
274+
275+
Assert.That(fileUpdater.GetCultureFromFileName(), Is.Null);
276+
}
77277
}
78278
}

SIL.BuildTasks.Tests/SIL.BuildTasks.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<!-- Don't remove NUnit.Runners.Net4 - this is needed for the NUnitTests fixture! -->
2424
<PackageReference Include="NUnit.Runners.Net4" Version="2.6.4" />
2525
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
26+
<PackageReference Include="SIL.TestUtilities" Version="7.0.0" />
2627
</ItemGroup>
2728
<ItemGroup>
2829
<ProjectReference Include="..\SIL.BuildTasks\SIL.BuildTasks.csproj" />

0 commit comments

Comments
 (0)