From 1059eb7885140f80bc822f2fbf8799b36a52e291 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 11 Nov 2026 13:36:13 -0500 Subject: [PATCH] test(heidenhain): live gate asserts integer tool leaves keep their runtime type (#867) Verified read-only on Hermle59 and Hermle70 (iTNC 530): Tool/Number Int64, Tool/Index and Tool/AxisCode Int32. Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk --- .../HeidenhainLiveIntegrationTests.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Heidenhain.Tests/HeidenhainLiveIntegrationTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Heidenhain.Tests/HeidenhainLiveIntegrationTests.cs index b0a9c4522..020425a31 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Heidenhain.Tests/HeidenhainLiveIntegrationTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Heidenhain.Tests/HeidenhainLiveIntegrationTests.cs @@ -85,4 +85,38 @@ public sealed class HeidenhainLiveIntegrationTests TestContext.Current.SendDiagnosticMessage($"{tags[i].RawPath} = {values[i].Value} (0x{values[i].StatusCode:X8})"); values[0].StatusCode.ShouldBe(HeidenhainStatus.Good); } + + /// #867: integer tool leaves must reach clients with their declared runtime type, not Double. + [Fact] + public async Task Real_control_tool_leaves_keep_integer_types() + { + Assert.SkipWhen(string.IsNullOrWhiteSpace(Host), "HEIDENHAIN_LSV2_HOST not set — real-controller gate skipped."); + var ct = TestContext.Current.CancellationToken; + var tags = new[] { TestTags.Raw("Tool/Number"), TestTags.Raw("Tool/Index"), TestTags.Raw("Tool/AxisCode") }; + var options = new HeidenhainDriverOptions + { + Devices = [new HeidenhainDeviceOptions { Host = Host!, Port = Port, DeviceName = "dev1" }], + RawTags = tags, + Access = new HeidenhainAccessOptions { InspectPassword = Environment.GetEnvironmentVariable("OTOPCUA_HEIDENHAIN_INSPECT_PASSWORD") is { Length: > 0 } ? "env:OTOPCUA_HEIDENHAIN_INSPECT_PASSWORD" : null }, + }; + await using var driver = new HeidenhainDriver(options, "live"); + await driver.InitializeAsync("{}", ct); + await driver.SubscribeAsync(tags.Select(t => t.RawPath).ToList(), TimeSpan.FromSeconds(1), ct); + var deadline = DateTime.UtcNow.AddSeconds(30); + IReadOnlyList values; + do + { + await Task.Delay(500, ct); + values = await driver.ReadAsync(tags.Select(t => t.RawPath).ToList(), ct); + } while (values.Any(v => v.StatusCode != HeidenhainStatus.Good) && DateTime.UtcNow < deadline); + + var expected = new[] { typeof(long), typeof(int), typeof(int) }; + for (var i = 0; i < tags.Length; i++) + { + TestContext.Current.SendDiagnosticMessage($"{tags[i].RawPath} = {values[i].Value} ({values[i].Value?.GetType().Name}) (0x{values[i].StatusCode:X8})"); + if (values[i].StatusCode == HeidenhainStatus.Good) + values[i].Value!.GetType().ShouldBe(expected[i]); + } + values.Count(v => v.StatusCode == HeidenhainStatus.Good).ShouldBeGreaterThan(0); + } }