using AIStudio.Tools;
using AIStudio.Tools.Rust;
namespace AIStudio.Tests.Tools;
///
/// Checks which file a code block becomes, judged by the language the model names behind its
/// opening fence.
///
[TestFixture]
public sealed class SourceCodeLanguagesTests
{
[TestCase("python", "Python (.py)")]
[TestCase("py", "Python (.py)")]
[TestCase("rust", "Rust (.rs)")]
[TestCase("rs", "Rust (.rs)")]
[TestCase("javascript", "JavaScript (.js)")]
[TestCase("js", "JavaScript (.js)")]
[TestCase("bash", "Shell (.sh)")]
[TestCase("sh", "Shell (.sh)")]
[TestCase("c#", "C# (.cs)")]
[TestCase("C++", "C++ (.cpp)")]
[TestCase("Python", "Python (.py)", Description = "Models do not agree on the case.")]
[TestCase(" yaml ", "YAML (.yaml)", Description = "Space around the name is no part of it.")]
public void AKnownLanguageNamesItsFile(string language, string expectedName)
{
Assert.That(SourceCodeLanguages.FromCodeFenceLanguage(language).ToName(), Is.EqualTo(expectedName));
}
[Test]
public void AnUnknownLanguageBecomesATextFileUnderItsOwnName()
{
var language = SourceCodeLanguages.FromCodeFenceLanguage("foo");
Assert.Multiple(() =>
{
Assert.That(language.Name, Is.EqualTo("foo"), "The user still has to recognize which block it is.");
Assert.That(language.FileExtension, Is.EqualTo(".txt"));
Assert.That(language.ToName(), Is.EqualTo("foo (.txt)"));
});
}
[TestCase(null, TestName = "A fence Markdig read no language for")]
[TestCase("", TestName = "A fence without a language")]
[TestCase(" ", TestName = "A language made of nothing")]
[TestCase("text", TestName = "A fence marked as text")]
[TestCase("PlainText", TestName = "A fence marked as plain text")]
public void PlainTextBecomesATextFile(string? language)
{
Assert.That(SourceCodeLanguages.FromCodeFenceLanguage(language).ToName(), Is.EqualTo("Text (.txt)"));
}
[Test]
public void AFileOfALanguageIsNamedAfterWhatItIsAbout()
{
var python = SourceCodeLanguages.FromCodeFenceLanguage("python");
Assert.Multiple(() =>
{
Assert.That(python.ToSuggestedFileName("Sort: a list?"), Is.EqualTo("Sort a list.py"));
Assert.That(python.ToSuggestedFileName(), Is.EqualTo("export.py"));
Assert.That(python.ToFileExtensionOrName(), Is.EqualTo(".py"));
Assert.That(python.ToFileTypeFilter().FlattenExtensions(), Is.EqualTo(new[] { "py" }), "The dialog offers the one extension we write, without its dot.");
});
}
[TestCase("dockerfile", "Dockerfile")]
[TestCase("Makefile", "Makefile")]
[TestCase("cmake", "CMakeLists.txt")]
public void AToolWhichLooksForAFixedFileNameGetsIt(string language, string expectedFileName)
{
var fixedName = SourceCodeLanguages.FromCodeFenceLanguage(language);
Assert.Multiple(() =>
{
Assert.That(fixedName.ToSuggestedFileName("Backend"), Is.EqualTo(expectedFileName), "The tool finds no file under another name, whatever the heading above the block says.");
Assert.That(fixedName.ToName(), Is.EqualTo(expectedFileName));
Assert.That(fixedName.ToFileExtensionOrName(), Is.EqualTo(expectedFileName));
Assert.That(FileTypes.ToFileDialogFilter(fixedName.ToFileTypeFilter()), Is.Null, "A file name is no extension, so the dialog shows every file.");
});
}
[Test]
public void OnlyPowerShellNeedsAByteOrderMark()
{
Assert.Multiple(() =>
{
Assert.That(SourceCodeLanguages.FromCodeFenceLanguage("powershell").ToFileEncoding().GetPreamble(), Is.Not.Empty, "Windows PowerShell 5.1 reads a script without one in the ANSI code page.");
Assert.That(SourceCodeLanguages.FromCodeFenceLanguage("python").ToFileEncoding().GetPreamble(), Is.Empty, "An interpreter has no use for it, and a shebang line breaks behind one.");
Assert.That(SourceCodeLanguages.Languages.Where(language => language.NeedsByteOrderMark).Select(language => language.Name), Is.EqualTo(new[] { "PowerShell" }));
});
}
[Test]
public void OnlyABatchFileNeedsWindowsLineEndings()
{
Assert.That(SourceCodeLanguages.Languages.Where(language => language.NeedsWindowsLineEndings).Select(language => language.Name), Is.EqualTo(new[] { "Batch" }), "The command prompt misses jump labels in a file whose lines end with LF alone.");
}
[Test]
public void EveryLanguageHasAFile()
{
Assert.Multiple(() =>
{
foreach (var language in SourceCodeLanguages.Languages)
{
Assert.That(language.Name, Is.Not.Empty);
Assert.That(language.FileExtension.Length is 0 ^ language.FileName.Length is 0, Is.True, $"{language.Name} needs either an extension or a fixed file name, not both.");
if (language.FileExtension.Length > 0)
Assert.That(language.FileExtension, Does.StartWith(".").And.Length.GreaterThan(1), $"{language.Name} has a broken extension.");
}
});
}
[Test]
public void NoAliasIsNamedTwice()
{
Assert.That(SourceCodeLanguages.Aliases, Is.Unique.Using((IEqualityComparer)StringComparer.OrdinalIgnoreCase), "Plain text is checked first, so a language with the same alias would never be found.");
}
[Test]
public void NoAliasNamesAFormatOfItsOwn()
{
// FileExportFormatExtensions.TryFromCodeFenceLanguage is asked first, so such an entry would never be found:
Assert.That(SourceCodeLanguages.Aliases.Where(alias => FileExportFormatExtensions.TryFromCodeFenceLanguage(alias, out _)), Is.Empty);
}
}