fix(ui): format stock sizes invariantly and bound validation notices
Address Task 3b quality review: - FormatSize now emits invariant decimal notation so a comma-decimal locale cannot produce stock text its own invariant parser rejects. - Validation notices embed a bounded single-line preview instead of the full cell value. - Documented GetPlateOptions' all-or-nothing throwing contract. - STA test helpers run the worker as a background thread with a diagnostic timeout message; added a de-DE fractional stock round-trip regression.
This commit is contained in:
@@ -55,10 +55,11 @@ public class ArchUnitsTests
|
||||
{
|
||||
try { action(); }
|
||||
catch (Exception ex) { error = ex; }
|
||||
});
|
||||
})
|
||||
{ IsBackground = true };
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
Assert.True(thread.Join(TimeSpan.FromSeconds(15)));
|
||||
Assert.True(thread.Join(TimeSpan.FromSeconds(15)), "The STA test did not complete.");
|
||||
if (error != null)
|
||||
ExceptionDispatchInfo.Capture(error).Throw();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -160,6 +161,31 @@ public class AutoNestFormStockOptionsTests
|
||||
grid.CancelEdit();
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public void FractionalStockSizesRoundTripUnderCommaDecimalCulture() => RunSta(() =>
|
||||
{
|
||||
var original = CultureInfo.CurrentCulture;
|
||||
try
|
||||
{
|
||||
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de-DE");
|
||||
using var form = new StockTestForm();
|
||||
// FormatSize must emit invariant dots or the reloaded collection would be
|
||||
// rejected as invalid under the invariant parser.
|
||||
form.LoadPlateOptions(new List<PlateOption>
|
||||
{
|
||||
new() { Width = 48.5, Length = 96.25, Cost = 25 },
|
||||
}, 0.5);
|
||||
Assert.True(form.TryGetPlateOptions(out var options, out var error));
|
||||
Assert.Null(error);
|
||||
Assert.Equal(48.5, options[0].Width);
|
||||
Assert.Equal(96.25, options[0].Length);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CultureInfo.CurrentCulture = original;
|
||||
}
|
||||
});
|
||||
|
||||
private sealed class StockTestForm : AutoNestForm
|
||||
{
|
||||
public StockTestForm() : base(new Nest()) { }
|
||||
@@ -212,10 +238,11 @@ public class AutoNestFormStockOptionsTests
|
||||
{
|
||||
try { action(); }
|
||||
catch (Exception ex) { error = ex; }
|
||||
});
|
||||
})
|
||||
{ IsBackground = true };
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
Assert.True(thread.Join(TimeSpan.FromSeconds(15)));
|
||||
Assert.True(thread.Join(TimeSpan.FromSeconds(15)), "The STA test did not complete.");
|
||||
if (error != null)
|
||||
ExceptionDispatchInfo.Capture(error).Throw();
|
||||
}
|
||||
|
||||
@@ -198,6 +198,11 @@ namespace OpenNest.Forms
|
||||
return nestItems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the validated stock collection. Throws <see cref="FormatException"/> when a
|
||||
/// nonblank row is invalid or timed out; all-or-nothing, never a valid prefix. Prefer
|
||||
/// <see cref="TryGetPlateOptions"/> for callers that must report the failure.
|
||||
/// </summary>
|
||||
public List<PlateOption> GetPlateOptions()
|
||||
{
|
||||
if (!TryGetPlateOptions(out var options, out var error))
|
||||
@@ -212,7 +217,7 @@ namespace OpenNest.Forms
|
||||
if (!plateGrid.EndEdit())
|
||||
{
|
||||
var value = plateGrid.EditingControl?.Text ?? plateGrid.CurrentCell?.Value?.ToString();
|
||||
error = $"Invalid stock size '{value}'. Enter positive dimensions as W x L.";
|
||||
error = $"Invalid stock size '{Preview(value)}'. Enter positive dimensions as W x L.";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -228,7 +233,7 @@ namespace OpenNest.Forms
|
||||
continue;
|
||||
if (!TryParseSize(item.Size, out var width, out var length))
|
||||
{
|
||||
error = $"Invalid stock size '{item.Size}' in row {index + 1}. Enter positive dimensions as W x L.";
|
||||
error = $"Invalid stock size '{Preview(item.Size)}' in row {index + 1}. Enter positive dimensions as W x L.";
|
||||
if (index < plateGrid.Rows.Count)
|
||||
plateGrid.Rows[index].ErrorText = error;
|
||||
return false;
|
||||
@@ -261,6 +266,16 @@ namespace OpenNest.Forms
|
||||
MessageBox.Show(this, error, "Auto Nest", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
|
||||
// Bounded single-line preview so a long or multiline cell value cannot produce an
|
||||
// unreadable message box; the full value stays in the editable cell.
|
||||
private static string Preview(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return string.Empty;
|
||||
var line = value.Replace("\r", " ").Replace("\n", " ");
|
||||
return line.Length <= 40 ? line : line[..40] + "...";
|
||||
}
|
||||
|
||||
public void LoadPlateOptions(List<PlateOption> options, double salvageRate)
|
||||
{
|
||||
if (options != null && options.Count > 0)
|
||||
@@ -396,7 +411,9 @@ namespace OpenNest.Forms
|
||||
|
||||
private static string FormatSize(double width, double length)
|
||||
{
|
||||
return $"{width:G} x {length:G}";
|
||||
// Invariant: TryParseSize requires invariant decimal notation; current-culture
|
||||
// formatting would produce comma decimals that the parser then rejects.
|
||||
return FormattableString.Invariant($"{width:G} x {length:G}");
|
||||
}
|
||||
|
||||
private void PartsGrid_DataError(object sender, DataGridViewDataErrorEventArgs e)
|
||||
|
||||
Reference in New Issue
Block a user