- FtpListParser: tolerant DOS/IIS regex (optional AM/PM, 2-or-4-digit year, 1-or-2-digit hour) for 24h/locale-dependent Mazak servers; NLST fallback now only accepts single-token lines (multi-token garbage/header -> skip) to avoid whole-line-as-filename. - MazakMachine.BuildTreeAsync: root-level LIST failure now propagates -> Fail (no more silent exit 0 on a diagnostic probe). Children keep best-effort inline "errore lista" swallow. Depth semantics preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
189 lines
6 KiB
C#
189 lines
6 KiB
C#
using NUnit.Framework;
|
|
using NcProgramManager.Cnc.Mazak;
|
|
|
|
namespace NcProgramManager.Tests.Unit
|
|
{
|
|
[TestFixture]
|
|
public class FtpListParserTests
|
|
{
|
|
// NormalizeDir
|
|
|
|
[Test]
|
|
public void NormalizeDir_Empty_ReturnsRoot()
|
|
{
|
|
Assert.That(FtpListParser.NormalizeDir(""), Is.EqualTo("/"));
|
|
}
|
|
|
|
[Test]
|
|
public void NormalizeDir_Null_ReturnsRoot()
|
|
{
|
|
Assert.That(FtpListParser.NormalizeDir(null), Is.EqualTo("/"));
|
|
}
|
|
|
|
[Test]
|
|
public void NormalizeDir_NoSlashes_AddsBoth()
|
|
{
|
|
Assert.That(FtpListParser.NormalizeDir("PRG"), Is.EqualTo("/PRG/"));
|
|
}
|
|
|
|
[Test]
|
|
public void NormalizeDir_NoTrailingSlash_AddsTrailing()
|
|
{
|
|
Assert.That(FtpListParser.NormalizeDir("/a/b"), Is.EqualTo("/a/b/"));
|
|
}
|
|
|
|
[Test]
|
|
public void NormalizeDir_AlreadyNormalized_Unchanged()
|
|
{
|
|
Assert.That(FtpListParser.NormalizeDir("/a/b/"), Is.EqualTo("/a/b/"));
|
|
}
|
|
|
|
// Parse - DOS/IIS format
|
|
|
|
[Test]
|
|
public void Parse_DosDir_DirectoryEntry()
|
|
{
|
|
var e = FtpListParser.Parse("07-18-26 10:00AM <DIR> MYDIR", "/PRG/");
|
|
|
|
Assert.That(e, Is.Not.Null);
|
|
Assert.That(e.Name, Is.EqualTo("MYDIR"));
|
|
Assert.That(e.IsDirectory, Is.True);
|
|
Assert.That(e.Size, Is.EqualTo(-1));
|
|
Assert.That(e.FullPath, Is.EqualTo("/PRG/MYDIR"));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_DosFile_FileEntryWithSize()
|
|
{
|
|
var e = FtpListParser.Parse("07-18-26 10:00AM 1234 O1000.EIA", "/PRG/");
|
|
|
|
Assert.That(e, Is.Not.Null);
|
|
Assert.That(e.Name, Is.EqualTo("O1000.EIA"));
|
|
Assert.That(e.IsDirectory, Is.False);
|
|
Assert.That(e.Size, Is.EqualTo(1234));
|
|
}
|
|
|
|
// Parse - DOS/IIS format, T4 tolerances (24h time, 4-digit year, spaces)
|
|
|
|
[Test]
|
|
public void Parse_Dos24hTime_DirectoryEntry()
|
|
{
|
|
// 24h time, no AM/PM: _dosLine \s*(AM|PM)? optional absorbs this.
|
|
var e = FtpListParser.Parse("07-18-26 22:00 <DIR> NIGHTDIR", "/PRG/");
|
|
|
|
Assert.That(e, Is.Not.Null);
|
|
Assert.That(e.Name, Is.EqualTo("NIGHTDIR"));
|
|
Assert.That(e.IsDirectory, Is.True);
|
|
Assert.That(e.Size, Is.EqualTo(-1));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_Dos4DigitYear_FileEntryWithSize()
|
|
{
|
|
// 4-digit year: date group is \d{2}-\d{2}-\d{2,4}.
|
|
var e = FtpListParser.Parse("07-18-2026 10:00AM 2048 O4000.EIA", "/PRG/");
|
|
|
|
Assert.That(e, Is.Not.Null);
|
|
Assert.That(e.Name, Is.EqualTo("O4000.EIA"));
|
|
Assert.That(e.IsDirectory, Is.False);
|
|
Assert.That(e.Size, Is.EqualTo(2048));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_DosFileNameWithSpaces_KeepsSpaces()
|
|
{
|
|
// name group is .+ (greedy to $) -> internal spaces preserved.
|
|
var e = FtpListParser.Parse("07-18-26 10:00AM 128 MY PROG.EIA", "/PRG/");
|
|
|
|
Assert.That(e, Is.Not.Null);
|
|
Assert.That(e.Name, Is.EqualTo("MY PROG.EIA"));
|
|
Assert.That(e.IsDirectory, Is.False);
|
|
Assert.That(e.Size, Is.EqualTo(128));
|
|
}
|
|
|
|
// Parse - Unix format
|
|
|
|
[Test]
|
|
public void Parse_UnixFileNameWithSpaces_KeepsSpaces()
|
|
{
|
|
// Unix name group is .+ -> internal spaces preserved.
|
|
var e = FtpListParser.Parse("-rw-r--r-- 1 own grp 64 Jul 18 10:00 MY PROG", "/PRG/");
|
|
|
|
Assert.That(e, Is.Not.Null);
|
|
Assert.That(e.Name, Is.EqualTo("MY PROG"));
|
|
Assert.That(e.IsDirectory, Is.False);
|
|
Assert.That(e.Size, Is.EqualTo(64));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_UnixDir_DirectoryEntry()
|
|
{
|
|
var e = FtpListParser.Parse("drwxr-xr-x 1 own grp 4096 Jul 18 10:00 sub", "/PRG/");
|
|
|
|
Assert.That(e, Is.Not.Null);
|
|
Assert.That(e.IsDirectory, Is.True);
|
|
Assert.That(e.Name, Is.EqualTo("sub"));
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_UnixFile_FileEntryWithSize()
|
|
{
|
|
var e = FtpListParser.Parse("-rw-r--r-- 1 own grp 512 Jul 18 10:00 O2000", "/PRG/");
|
|
|
|
Assert.That(e, Is.Not.Null);
|
|
Assert.That(e.IsDirectory, Is.False);
|
|
Assert.That(e.Size, Is.EqualTo(512));
|
|
Assert.That(e.Name, Is.EqualTo("O2000"));
|
|
}
|
|
|
|
// Parse - skip '.' and '..'
|
|
|
|
[Test]
|
|
public void Parse_DotDir_ReturnsNull()
|
|
{
|
|
var e = FtpListParser.Parse("07-18-26 10:00AM <DIR> .", "/PRG/");
|
|
|
|
Assert.That(e, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void Parse_DotDotDir_ReturnsNull()
|
|
{
|
|
var e = FtpListParser.Parse("07-18-26 10:00AM <DIR> ..", "/PRG/");
|
|
|
|
Assert.That(e, Is.Null);
|
|
}
|
|
|
|
// Parse - fallback (NLST bare name)
|
|
|
|
[Test]
|
|
public void Parse_BareName_FallbackFileEntry()
|
|
{
|
|
var e = FtpListParser.Parse("O3000", "/PRG/");
|
|
|
|
Assert.That(e, Is.Not.Null);
|
|
Assert.That(e.Name, Is.EqualTo("O3000"));
|
|
Assert.That(e.IsDirectory, Is.False);
|
|
Assert.That(e.Size, Is.EqualTo(-1));
|
|
}
|
|
|
|
// Parse - garbage/header multi-token line matches neither regex -> skip
|
|
|
|
[Test]
|
|
public void Parse_GarbageMultiToken_ReturnsNull()
|
|
{
|
|
// "total 8": DOS regex needs leading MM-DD-YY date (no); Unix perm group is
|
|
// [dl-][rwxsStT-]{9} and 't' is not in [dl-] (no). Trimmed line has internal
|
|
// whitespace -> T4 fallback returns null (not treated as bare NLST name).
|
|
Assert.That(FtpListParser.Parse("total 8", "/PRG/"), Is.Null);
|
|
}
|
|
|
|
// Parse - whitespace/empty
|
|
|
|
[Test]
|
|
public void Parse_Whitespace_ReturnsNull()
|
|
{
|
|
Assert.That(FtpListParser.Parse(" ", "/PRG/"), Is.Null);
|
|
}
|
|
}
|
|
}
|