Fixed line endings in the reflection folder and tests on Windows (#1192)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-11-11 17:15:12 +01:00
1 parent 6f187f1bd8
commit b1c1f13993
7 files changed
+157 -22

No files matched your search

@@ -46,8 +46,8 @@ public sealed class BflPromptComposerTests
using (Assert.EnterMultipleScope())
{
Assert.That(prompt, Does.Not.Contain("{{"), step.ToString());
Assert.That(prompt, Does.Contain(parts.Step.Trim()), step.ToString());
Assert.That(prompt, Does.Contain(parts.Knowledge.Trim()[..200]), step.ToString());
Assert.That(prompt, Does.Contain(WithLf(parts.Step)), step.ToString());
Assert.That(prompt, Does.Contain(WithLf(parts.Knowledge)[..200]), step.ToString());
}
}
}
@@ -79,7 +79,7 @@ public sealed class BflPromptComposerTests
{
Assert.That(prompt, Does.Not.Contain("{{"));
Assert.That(prompt, Does.Contain("Antworte auf Deutsch."));
Assert.That(prompt, Does.Not.Contain(knowledge.Trim()[..200]), "The onboarding stays short, so it leaves out the guide.");
Assert.That(prompt, Does.Not.Contain(WithLf(knowledge)[..200]), "The onboarding stays short, so it leaves out the guide.");
}
}
@@ -279,7 +279,7 @@ public sealed class BflPromptComposerTests
using (Assert.EnterMultipleScope())
{
Assert.That(prompt.IndexOf(parts.Step.Trim(), StringComparison.Ordinal), Is.LessThan(material), "The material changes less often than the details, so it comes first for the prompt caching.");
Assert.That(prompt.IndexOf(WithLf(parts.Step), StringComparison.Ordinal), Is.GreaterThanOrEqualTo(0).And.LessThan(material), "The material changes less often than the details, so it comes first for the prompt caching.");
Assert.That(material, Is.LessThan(prompt.IndexOf("THESE-4711", StringComparison.Ordinal)));
}
}
@@ -330,4 +330,14 @@ public sealed class BflPromptComposerTests
}
private static BflMaterialExcerpt Excerpt(string name, string text, int textLength) => new(new(new BflMaterial { MaterialId = Guid.NewGuid(), Name = name, TextLength = textLength }, textLength), text);
/// <summary>
/// Gets a file of the lab's prompts as the composer inserts it: trimmed and with LF line breaks.
/// </summary>
/// <remarks>
/// The files keep the line endings of the checkout, CR LF on Windows with core.autocrlf. Compared as they are, a
/// check for a part fails there, and a check for its absence passes without checking anything.
/// </remarks>
/// <param name="text">The text of the file.</param>
private static string WithLf(string text) => text.ReplaceLineEndings("\n").Trim();
}
@@ -174,6 +174,27 @@ public sealed class BflReflectionFolderTests
Assert.That(folder.Split('\n')[0], Does.StartWith("# ").And.Contain("Panel").And.Contain("Kopf"));
}
[Test]
public void EveryLineEndsWithLfAlsoWhenATextComesWithCrLf()
{
var workStates = new Dictionary<BflStep, BflWorkState>();
Confirm(workStates, BflStep.KNOWLEDGE, new() { ["ESTABLISHED"] = "GESICHERT-1\r\nGESICHERT-2", ["OPEN_POINTS"] = "OFFEN-1\r\nOFFEN-2" });
var conversations = new Dictionary<BflStep, IReadOnlyList<BflConversationMessage>>
{
[BflStep.STRATEGY] = [new(false, "ANTWORT-1\r\n\r\nANTWORT-2")],
};
var folder = Build(workStates, conversations: conversations);
using (Assert.EnterMultipleScope())
{
Assert.That(folder, Does.Not.Contain("\r"), "The folder must never mix two kinds of line breaks, also not on Windows.");
Assert.That(folder, Does.Contain("GESICHERT-1\nGESICHERT-2\n"));
Assert.That(folder, Does.Contain("OFFEN-1\nOFFEN-2\n"));
Assert.That(folder, Does.Contain("ANTWORT-1\n\nANTWORT-2\n"));
}
}
private static string Build(IReadOnlyDictionary<BflStep, BflWorkState> workStates, bool includePersonalDisclosures = false, IReadOnlyDictionary<BflStep, IReadOnlyList<BflConversationMessage>>? conversations = null) => BflReflectionFolder.Build(new BflReflectionFolderInput("Panel", workStates, Forms(), [], conversations ?? new Dictionary<BflStep, IReadOnlyList<BflConversationMessage>>(), includePersonalDisclosures, CultureInfo.InvariantCulture));
private static Dictionary<BflStep, BflStepFormData> Forms() => BflStep.All.ToDictionary(step => step, _ => new BflStepFormData());
@@ -101,7 +101,8 @@ public sealed class I18NSourceScannerTests
var texts = I18NSourceScanner.FindTexts(SOURCE, "Notice.cs", []);
Assert.That(texts.Select(text => text.Text), Is.EqualTo(new[] { "## Welcome!\n\nThe first paragraph." }));
// The scanner keeps the line endings of the checkout, as the compiler does, and the collector makes them equal:
Assert.That(texts.Select(text => I18NKeyHash.NormalizeLineEndings(text.Text)), Is.EqualTo(new[] { "## Welcome!\n\nThe first paragraph." }));
}
[Test]
@@ -0,0 +1,46 @@
using System.Globalization;
using System.Text;
using SharedTools;
namespace AIStudio.Tests.Tools;
/// <summary>
/// Checks that AppendLineLF ends every line with LF, whatever the system.
/// </summary>
/// <remarks>
/// StringBuilder.AppendLine writes CR LF on Windows. These tests would show it there, while on macOS and Linux they
/// pass with both.
/// </remarks>
[TestFixture]
public sealed class StringBuilderExtensionsTests
{
[Test]
public void EveryKindOfLineEndsWithLf()
{
var number = 4711;
var text = new StringBuilder()
.AppendLineLF("Erste Zeile")
.AppendLineLF()
.AppendLineLF($"Nummer {number}")
.AppendLineLF(null)
.ToString();
Assert.That(text, Is.EqualTo("Erste Zeile\n\nNummer 4711\n\n"));
}
[Test]
public void AnInterpolatedTextGoesIntoTheSameBuilderInItsPlace()
{
var value = 1.5;
var builder = new StringBuilder("Anfang ");
var returned = builder.AppendLineLF($"{value.ToString(CultureInfo.InvariantCulture)} und {"Text"}").Append("Ende");
using (Assert.EnterMultipleScope())
{
Assert.That(returned, Is.SameAs(builder));
Assert.That(builder.ToString(), Is.EqualTo("Anfang 1.5 und Text\nEnde"));
}
}
}