Skip to content

Added support for date insertion in localized files #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added
- [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.

### Changed

- [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.
- [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.

### Deprecated
Expand Down
202 changes: 201 additions & 1 deletion SIL.BuildTasks.Tests/FileUpdateTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// Copyright (c) 2024 SIL Global
// Copyright (c) 2025 SIL Global
// This software is licensed under the MIT License (http://opensource.org/licenses/MIT)

using System;
using NUnit.Framework;
using SIL.Providers;
using SIL.TestUtilities.Providers;

// Sadly, Resharper wants to change Is.EqualTo to NUnit.Framework.Is.EqualTo
// ReSharper disable AccessToStaticMemberViaDerivedType

Expand All @@ -11,6 +14,19 @@ namespace SIL.BuildTasks.Tests
[TestFixture]
public class FileUpdateTests
{
[SetUp]
public void Setup()
{
DateTimeProvider.SetProvider(
new ReproducibleDateTimeProvider(new DateTime(2026, 4, 16)));
}

[TearDown]
public void TearDown()
{
DateTimeProvider.ResetToDefault();
}

[TestCase("This is the story of the frog prince.", "frog", "monkey",
ExpectedResult = "This is the story of the monkey prince.")]
[TestCase("This is the story of the frog prince.", "f[^ ]+g", "toad",
Expand Down Expand Up @@ -62,6 +78,106 @@ public void GetModifiedContents_RegexTextNotMatched_Throws(string origContents,
Assert.That(ex.Message, Is.EqualTo($"No replacements made. Regex: '{regex}'; ReplacementText: '{replacement}'"));
}

[TestCase("_DATE_ _VERSION_\r\nStuff", "_DATE_", "M/yyyy", "4/2026 3.2.1\r\nStuff")]
[TestCase("_DATE_ _VERSION_\r\nStuff done before _DATE_", "_DATE_", "M/yyyy", "4/2026 3.2.1\r\nStuff done before 4/2026")]
[TestCase("&DATE; _VERSION_\r\n- point #1", "&DATE;", "dd-MM-yy", "16-04-26 3.2.1\r\n- point #1")]
[TestCase("DATE _VERSION_", "DATE", "dd MMMM, yyyy", "16 April, 2026 3.2.1")]
[TestCase("DATE _VERSION_", "DATE", null, "16/Apr/2026 3.2.1")] // Uses default date format
public void GetModifiedContents_DateLiteral_InsertsDateWithSpecifiedDateFormat(
string origContents, string datePlaceholder, string dateFormat,
string expectedResult)
{
var updater = new FileUpdate
{
Regex = "_VERSION_",
ReplacementText = "3.2.1",
DatePlaceholder = datePlaceholder,
DateFormat = dateFormat
};

var result = updater.GetModifiedContents(origContents);
Assert.That(result, Is.EqualTo(expectedResult));
}

[TestCase("_DATE_ _VERSION_\r\nStuff", "M/yyyy", "4/2026 3.2.1\r\nStuff")]
[TestCase("_DATE_ _VERSION_\r\nStuff done before _DATE_", "dd-MM-yy", "16-04-26 3.2.1\r\nStuff done before 16-04-26")]
public void GetModifiedContents_SpecialDatePlaceholderButFileDoesNotSpecifyFormat_InsertsDateWithSpecifiedDateFormat(
string origContents, string dateFormat, string expectedResult)
{
var updater = new FileUpdate
{
Regex = "_VERSION_",
ReplacementText = "3.2.1",
DatePlaceholder = "_DATE(*)_",
DateFormat = dateFormat
};

var result = updater.GetModifiedContents(origContents);
Assert.That(result, Is.EqualTo(expectedResult));
}

[TestCase("MM-yy")]
[TestCase("dd MMMM")]
public void GetModifiedContents_SpecialDatePlaceholderWithFileSpecifyingFormat_InsertsDateWithFormatFromFile(
string format)
{
var origContents = $"_DATE({format})_\r\nStuff";

var updater = new FileUpdate
{
Regex = "(.*)",
ReplacementText = "$1",
DatePlaceholder = "_DATE(*)_",
};

var currentDate = DateTimeProvider.Current.UtcNow.ToString(format);

var result = updater.GetModifiedContents(origContents);
Assert.That(result, Is.EqualTo($"{currentDate}\r\nStuff"));
}

[TestCase("MM-yyyy", "d MMMM yy")]
[TestCase("dd MMMM", "MM/dd/yyyy")]
public void GetModifiedContents_SpecialDatePlaceholderWithFileSpecifyingMultipleFormats_InsertsDateWithFormatsFromFile(
string format1, string format2)
{
var origContents = $"First _DATE({format1})_\r\nSecond _DATE_\r\nLast _DATE({format2})_";

var updater = new FileUpdate
{
Regex = "(.*)",
ReplacementText = "$1",
DatePlaceholder = "_DATE(*)_",
};

var currentDate = DateTimeProvider.Current.UtcNow;
var currentDate1 = currentDate.ToString(format1);
var currentDate2 = currentDate.ToString(format2);

var result = updater.GetModifiedContents(origContents);
Assert.That(result, Is.EqualTo($"First {currentDate1}\r\nSecond 16/Apr/2026\r\nLast {currentDate2}"));
}

[TestCase("es", "abril")]
[TestCase("fr", "avril")]
public void GetModifiedContents_SpecialDatePlaceholderWithLocalizedFileSpecifyingFormat_InsertsLocaleSpecificDateWithFormatFromFile(
string locale, string localizedMonthName)
{
var origContents = "_DATE(d MMMM yyyy)_\r\nStuff";

var updater = new FileUpdate
{
File = $"ReleaseNotes.{locale}.md",
FileLocalePattern = @"\.(?<locale>[a-z]{2}(-\w+)?)\.md$",
Regex = "(.*)",
ReplacementText = "$1",
DatePlaceholder = "_DATE(*)_",
};

var result = updater.GetModifiedContents(origContents);
Assert.That(result, Is.EqualTo($"16 {localizedMonthName} 2026\r\nStuff"));
}

[Test]
public void GetModifiedContents_InvalidRegex_Throws()
{
Expand All @@ -74,5 +190,89 @@ public void GetModifiedContents_InvalidRegex_Throws()
var ex = Assert.Throws<Exception>(() => updater.GetModifiedContents("Whatever"));
Assert.That(ex.Message, Is.EqualTo($"Invalid regular expression: parsing \"{updater.Regex}\" - Not enough )'s."));
}

[Test]
public void FileLocalePattern_InvalidRegex_ThrowsArgumentException()
{
const string expr = @"ReleaseNotes\.(.*\.md";
Assert.That(() =>
{
_ = new FileUpdate
{
FileLocalePattern = expr,
ReplacementText = "oops"
};
}, Throws.ArgumentException.With.Message.EqualTo($"FileLocalePattern: Invalid regular expression: parsing \"{expr}\" - Not enough )'s."));
}

[TestCase("es")]
[TestCase("fr")]
[TestCase("zh-CN")]
public void GetCultureFromFileName_MatchLocaleGroupToKnownCulture_GetsSpecifiedCulture(string localeSpecifier)
{
var fileUpdater = new FileUpdate
{
File = $"ReleaseNotes.{localeSpecifier}.md",
FileLocalePattern = @"\.(?<locale>[a-z]{2}(-\w+)?)\.md$",
};

Assert.That(fileUpdater.GetCultureFromFileName().IetfLanguageTag,
Is.EqualTo(localeSpecifier));
}

[TestCase("zz-Unknown")]
[TestCase("qq-Weird")]
public void GetCultureFromFileName_MatchLocaleGroupToUnknownCulture_ReturnsNull(string localeSpecifier)
{
var fileUpdater = new FileUpdate
{
File = $"ReleaseNotes.{localeSpecifier}.md",
FileLocalePattern = @"\.(?<locale>[a-z]{2}(-\w+)?)\.md$",
};

Assert.That(fileUpdater.GetCultureFromFileName(), Is.Null);
}

[TestCase("es")]
[TestCase("fr-FR")]
[TestCase("de")]
public void GetCultureFromFileName_EntireMatchIsKnownCulture_GetsSpecifiedCulture(string localeSpecifier)
{
var fileUpdater = new FileUpdate
{
File = $"ReleaseNotes.{localeSpecifier}.md",
FileLocalePattern = @"(?<=\.)es|fr-FR|de(?=\.)",
};

Assert.That(fileUpdater.GetCultureFromFileName().IetfLanguageTag,
Is.EqualTo(localeSpecifier));
}

[TestCase("My.bat.ate.your.homework.md", @"(?<=\.)[a-z]{4}(?=\.)")]
[TestCase("ReleaseNotes.htm", ".+")]
public void GetCultureFromFileName_EntireMatchIsUnknownCulture_ReturnsNull(string fileName, string pattern)
{
var fileUpdater = new FileUpdate
{
File = fileName,
FileLocalePattern = pattern,
};

Assert.That(fileUpdater.GetCultureFromFileName(), Is.Null);
}

[TestCase("My.bat.ate.your.homework.md", @"(?<=\.)[a-z]{22}(?=\.)")]
[TestCase("ReleaseNotes.htm", @"(?<=\.)es|fr-FR|de(?=\.)")]
[TestCase("ReleaseNotes.htm", @"\.(?<locale>[a-z]{2}(-\w+)?)\.md$")]
public void GetCultureFromFileName_NoMatch_ReturnsNull(string fileName, string pattern)
{
var fileUpdater = new FileUpdate
{
File = fileName,
FileLocalePattern = pattern,
};

Assert.That(fileUpdater.GetCultureFromFileName(), Is.Null);
}
}
}
1 change: 1 addition & 0 deletions SIL.BuildTasks.Tests/SIL.BuildTasks.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<!-- Don't remove NUnit.Runners.Net4 - this is needed for the NUnitTests fixture! -->
<PackageReference Include="NUnit.Runners.Net4" Version="2.6.4" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="SIL.TestUtilities" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SIL.BuildTasks\SIL.BuildTasks.csproj" />
Expand Down
Loading
Loading