Auto: s7-d1 — TIA Portal CSV + STEP 7 Classic AWL symbol import
Closes #299
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Infrastructure;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.S7.Cli.Commands;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.S7.Cli.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Coverage for the <c>import-symbols</c> CLI command. The command is intentionally
|
||||
/// thin (open file, hand to <c>TiaCsvImporter</c> / <c>AwlImporter</c>, serialise) —
|
||||
/// these tests focus on the I/O + flag-handling shape rather than re-running the
|
||||
/// parser.
|
||||
/// </summary>
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class ImportSymbolsCommandTests
|
||||
{
|
||||
private const string CanonicalTiaCsv = """
|
||||
Name,Path,Data type,Logical address,Comment,Hmi accessible
|
||||
MotorSpeed,Default,Int,%MW0,Motor speed,True
|
||||
TankLevel,Default,Real,%MD4,Tank level,True
|
||||
RunFlag,Default,Bool,%M0.0,Run flag,True
|
||||
""";
|
||||
|
||||
private const string CanonicalAwl = """
|
||||
VAR_GLOBAL
|
||||
Speed : INT;
|
||||
Pressure : REAL;
|
||||
END_VAR
|
||||
""";
|
||||
|
||||
[Fact]
|
||||
public async Task Execute_with_tia_csv_emits_json_fragment_with_three_tags()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"s7-tia-{Guid.NewGuid():N}.csv");
|
||||
File.WriteAllText(path, CanonicalTiaCsv);
|
||||
try
|
||||
{
|
||||
using var console = new FakeInMemoryConsole();
|
||||
var cmd = new ImportSymbolsCommand
|
||||
{
|
||||
File = path,
|
||||
Format = "tia",
|
||||
Emit = "appsettings-fragment",
|
||||
};
|
||||
|
||||
await cmd.ExecuteAsync(console);
|
||||
|
||||
var output = console.ReadOutputString();
|
||||
output.ShouldContain("\"Tags\"");
|
||||
|
||||
using var doc = JsonDocument.Parse(output);
|
||||
var tags = doc.RootElement.GetProperty("Tags");
|
||||
tags.GetArrayLength().ShouldBe(3);
|
||||
tags[0].GetProperty("Name").GetString().ShouldBe("MotorSpeed");
|
||||
tags[0].GetProperty("Address").GetString().ShouldBe("MW0");
|
||||
tags[0].GetProperty("DataType").GetString().ShouldBe("Int16");
|
||||
tags[2].GetProperty("DataType").GetString().ShouldBe("Bool");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { File.Delete(path); } catch { /* best-effort */ }
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Execute_with_summary_emit_prints_counters()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"s7-tia-{Guid.NewGuid():N}.csv");
|
||||
File.WriteAllText(path, CanonicalTiaCsv);
|
||||
try
|
||||
{
|
||||
using var console = new FakeInMemoryConsole();
|
||||
var cmd = new ImportSymbolsCommand
|
||||
{
|
||||
File = path,
|
||||
Format = "tia",
|
||||
Emit = "summary",
|
||||
};
|
||||
|
||||
await cmd.ExecuteAsync(console);
|
||||
|
||||
var output = console.ReadOutputString();
|
||||
output.ShouldContain("Imported 3");
|
||||
output.ShouldContain("udt-placeholders 0");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { File.Delete(path); } catch { /* best-effort */ }
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Execute_with_awl_format_emits_var_global_tags()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"s7-awl-{Guid.NewGuid():N}.awl");
|
||||
File.WriteAllText(path, CanonicalAwl);
|
||||
try
|
||||
{
|
||||
using var console = new FakeInMemoryConsole();
|
||||
var cmd = new ImportSymbolsCommand
|
||||
{
|
||||
File = path,
|
||||
Format = "awl",
|
||||
Emit = "appsettings-fragment",
|
||||
};
|
||||
|
||||
await cmd.ExecuteAsync(console);
|
||||
|
||||
var output = console.ReadOutputString();
|
||||
using var doc = JsonDocument.Parse(output);
|
||||
var tags = doc.RootElement.GetProperty("Tags");
|
||||
tags.GetArrayLength().ShouldBe(2);
|
||||
tags[0].GetProperty("Address").GetString().ShouldBe("MW0");
|
||||
tags[1].GetProperty("Address").GetString().ShouldBe("MD2");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { File.Delete(path); } catch { /* best-effort */ }
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Execute_with_output_path_writes_file_and_prints_summary()
|
||||
{
|
||||
var inputPath = Path.Combine(Path.GetTempPath(), $"s7-tia-{Guid.NewGuid():N}.csv");
|
||||
var outputPath = Path.Combine(Path.GetTempPath(), $"s7-tia-{Guid.NewGuid():N}.json");
|
||||
File.WriteAllText(inputPath, CanonicalTiaCsv);
|
||||
try
|
||||
{
|
||||
using var console = new FakeInMemoryConsole();
|
||||
var cmd = new ImportSymbolsCommand
|
||||
{
|
||||
File = inputPath,
|
||||
Format = "tia",
|
||||
Emit = "appsettings-fragment",
|
||||
Output = outputPath,
|
||||
};
|
||||
|
||||
await cmd.ExecuteAsync(console);
|
||||
|
||||
File.Exists(outputPath).ShouldBeTrue();
|
||||
var fileBody = File.ReadAllText(outputPath);
|
||||
using var doc = JsonDocument.Parse(fileBody);
|
||||
doc.RootElement.GetProperty("Tags").GetArrayLength().ShouldBe(3);
|
||||
|
||||
console.ReadOutputString().ShouldContain("Wrote 3");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { File.Delete(inputPath); } catch { /* best-effort */ }
|
||||
try { File.Delete(outputPath); } catch { /* best-effort */ }
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Execute_with_missing_file_throws_command_exception()
|
||||
{
|
||||
var missing = Path.Combine(Path.GetTempPath(), $"does-not-exist-{Guid.NewGuid():N}.csv");
|
||||
using var console = new FakeInMemoryConsole();
|
||||
var cmd = new ImportSymbolsCommand
|
||||
{
|
||||
File = missing,
|
||||
};
|
||||
|
||||
var ex = await Should.ThrowAsync<CommandException>(async () => await cmd.ExecuteAsync(console));
|
||||
ex.ExitCode.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Execute_with_unknown_format_throws_command_exception()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"s7-tia-{Guid.NewGuid():N}.csv");
|
||||
File.WriteAllText(path, CanonicalTiaCsv);
|
||||
try
|
||||
{
|
||||
using var console = new FakeInMemoryConsole();
|
||||
var cmd = new ImportSymbolsCommand
|
||||
{
|
||||
File = path,
|
||||
Format = "yaml",
|
||||
};
|
||||
|
||||
var ex = await Should.ThrowAsync<CommandException>(async () => await cmd.ExecuteAsync(console));
|
||||
ex.ExitCode.ShouldBe(2);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { File.Delete(path); } catch { /* best-effort */ }
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Execute_with_unknown_emit_throws_command_exception()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"s7-tia-{Guid.NewGuid():N}.csv");
|
||||
File.WriteAllText(path, CanonicalTiaCsv);
|
||||
try
|
||||
{
|
||||
using var console = new FakeInMemoryConsole();
|
||||
var cmd = new ImportSymbolsCommand
|
||||
{
|
||||
File = path,
|
||||
Format = "tia",
|
||||
Emit = "xml",
|
||||
};
|
||||
|
||||
var ex = await Should.ThrowAsync<CommandException>(async () => await cmd.ExecuteAsync(console));
|
||||
ex.ExitCode.ShouldBe(2);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { File.Delete(path); } catch { /* best-effort */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user