Skip to content

Commit 26951e2

Browse files
committed
Merge branch 'v2_develop' into v2_open-child-process-with-return-result
2 parents a9e1b4a + b9f55a5 commit 26951e2

File tree

163 files changed

+4851
-2939
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

163 files changed

+4851
-2939
lines changed

.github/workflows/unit-tests.yml

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ jobs:
120120
matrix:
121121
os: [ ubuntu-latest, windows-latest, macos-latest ]
122122

123-
timeout-minutes: 15
123+
timeout-minutes: 60
124124
steps:
125125

126126
- name: Checkout code
@@ -154,35 +154,81 @@ jobs:
154154
shell: bash
155155
run: echo "VSTEST_DUMP_PATH=logs/UnitTestsParallelizable/${{ runner.os }}/" >> $GITHUB_ENV
156156

157-
- name: Run UnitTestsParallelizable
157+
- name: Run UnitTestsParallelizable (10 iterations with varying parallelization)
158158
shell: bash
159159
run: |
160-
if [ "${{ runner.os }}" == "Linux" ]; then
161-
# Run with coverage on Linux only
162-
dotnet test Tests/UnitTestsParallelizable \
163-
--no-build \
164-
--verbosity normal \
165-
--collect:"XPlat Code Coverage" \
166-
--settings Tests/UnitTests/runsettings.coverage.xml \
167-
--diag:logs/UnitTestsParallelizable/${{ runner.os }}/logs.txt \
168-
--blame \
169-
--blame-crash \
170-
--blame-hang \
171-
--blame-hang-timeout 60s \
172-
--blame-crash-collect-always
173-
else
174-
# Run without coverage on Windows/macOS for speed
175-
dotnet test Tests/UnitTestsParallelizable \
176-
--no-build \
177-
--verbosity normal \
178-
--settings Tests/UnitTestsParallelizable/runsettings.xml \
179-
--diag:logs/UnitTestsParallelizable/${{ runner.os }}/logs.txt \
180-
--blame \
181-
--blame-crash \
182-
--blame-hang \
183-
--blame-hang-timeout 60s \
184-
--blame-crash-collect-always
185-
fi
160+
# Run tests 10 times with different parallelization settings to expose concurrency issues
161+
for RUN in {1..10}; do
162+
echo "============================================"
163+
echo "Starting test run $RUN of 10"
164+
echo "============================================"
165+
166+
# Use a combination of run number and timestamp to create different execution patterns
167+
SEED=$((1000 + $RUN + $(date +%s) % 1000))
168+
echo "Using randomization seed: $SEED"
169+
170+
# Vary the xUnit parallelization based on run number to expose race conditions
171+
# Runs 1-3: Default parallelization (2x CPU cores)
172+
# Runs 4-6: Max parallelization (unlimited)
173+
# Runs 7-9: Single threaded (1)
174+
# Run 10: Random (1-4 threads)
175+
if [ $RUN -le 3 ]; then
176+
XUNIT_MAX_PARALLEL_THREADS="2x"
177+
echo "Run $RUN: Using default parallelization (2x)"
178+
elif [ $RUN -le 6 ]; then
179+
XUNIT_MAX_PARALLEL_THREADS="unlimited"
180+
echo "Run $RUN: Using maximum parallelization (unlimited)"
181+
elif [ $RUN -le 9 ]; then
182+
XUNIT_MAX_PARALLEL_THREADS="1"
183+
echo "Run $RUN: Using single-threaded execution"
184+
else
185+
# Random parallelization based on seed
186+
PROC_COUNT=$(( ($SEED % 4) + 1 ))
187+
XUNIT_MAX_PARALLEL_THREADS="$PROC_COUNT"
188+
echo "Run $RUN: Using random parallelization with $PROC_COUNT threads"
189+
fi
190+
191+
# Run tests with or without coverage based on OS and run number
192+
if [ "${{ runner.os }}" == "Linux" ] && [ $RUN -eq 1 ]; then
193+
echo "Run $RUN: Running with coverage collection"
194+
dotnet test Tests/UnitTestsParallelizable \
195+
--no-build \
196+
--verbosity normal \
197+
--collect:"XPlat Code Coverage" \
198+
--settings Tests/UnitTests/runsettings.coverage.xml \
199+
--diag:logs/UnitTestsParallelizable/${{ runner.os }}/run${RUN}-logs.txt \
200+
--blame \
201+
--blame-crash \
202+
--blame-hang \
203+
--blame-hang-timeout 60s \
204+
--blame-crash-collect-always \
205+
-- xUnit.MaxParallelThreads=${XUNIT_MAX_PARALLEL_THREADS}
206+
else
207+
dotnet test Tests/UnitTestsParallelizable \
208+
--no-build \
209+
--verbosity normal \
210+
--settings Tests/UnitTestsParallelizable/runsettings.xml \
211+
--diag:logs/UnitTestsParallelizable/${{ runner.os }}/run${RUN}-logs.txt \
212+
--blame \
213+
--blame-crash \
214+
--blame-hang \
215+
--blame-hang-timeout 60s \
216+
--blame-crash-collect-always \
217+
-- xUnit.MaxParallelThreads=${XUNIT_MAX_PARALLEL_THREADS}
218+
fi
219+
220+
if [ $? -ne 0 ]; then
221+
echo "ERROR: Test run $RUN failed!"
222+
exit 1
223+
fi
224+
225+
echo "Test run $RUN completed successfully"
226+
echo ""
227+
done
228+
229+
echo "============================================"
230+
echo "All 10 test runs completed successfully!"
231+
echo "============================================"
186232
187233
- name: Upload UnitTestsParallelizable Logs
188234
if: always()

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,4 @@ log.*
7373
!/Tests/coverage/.gitkeep # keep folder in repo
7474
/Tests/report/
7575
*.cobertura.xml
76+
/docfx/docs/migratingfromv1.md

Examples/Example/Example.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,13 @@ public ExampleWindow ()
7777
{
7878
if (userNameText.Text == "admin" && passwordText.Text == "password")
7979
{
80-
MessageBox.Query ("Logging In", "Login Successful", "Ok");
80+
MessageBox.Query (App, "Logging In", "Login Successful", "Ok");
8181
UserName = userNameText.Text;
8282
Application.RequestStop ();
8383
}
8484
else
8585
{
86-
MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
86+
MessageBox.ErrorQuery (App, "Logging In", "Incorrect username or password", "Ok");
8787
}
8888
// When Accepting is handled, set e.Handled to true to prevent further processing.
8989
e.Handled = true;

Examples/NativeAot/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,13 @@ public ExampleWindow ()
101101
{
102102
if (userNameText.Text == "admin" && passwordText.Text == "password")
103103
{
104-
MessageBox.Query ("Logging In", "Login Successful", "Ok");
104+
MessageBox.Query (App, "Logging In", "Login Successful", "Ok");
105105
UserName = userNameText.Text;
106106
Application.RequestStop ();
107107
}
108108
else
109109
{
110-
MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
110+
MessageBox.ErrorQuery (App, "Logging In", "Incorrect username or password", "Ok");
111111
}
112112
// Anytime Accepting is handled, make sure to set e.Handled to true.
113113
e.Handled = true;

Examples/RunnableWrapperExample/Program.cs

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,41 +13,42 @@
1313
textField.Title = "Enter your name";
1414
textField.BorderStyle = LineStyle.Single;
1515

16-
var textRunnable = textField.AsRunnable (tf => tf.Text);
16+
RunnableWrapper<TextField, string> textRunnable = textField.AsRunnable (tf => tf.Text);
1717
app.Run (textRunnable);
1818

1919
if (textRunnable.Result is { } name)
2020
{
21-
MessageBox.Query ("Result", $"You entered: {name}", "OK");
21+
MessageBox.Query (app, "Result", $"You entered: {name}", "OK");
2222
}
2323
else
2424
{
25-
MessageBox.Query ("Result", "Canceled", "OK");
25+
MessageBox.Query (app, "Result", "Canceled", "OK");
2626
}
27+
2728
textRunnable.Dispose ();
2829

2930
// Example 2: Use IApplication.RunView() for one-liner
30-
var selectedColor = app.RunView (
31-
new ColorPicker
32-
{
33-
Title = "Pick a Color",
34-
BorderStyle = LineStyle.Single
35-
},
36-
cp => cp.SelectedColor);
31+
Color selectedColor = app.RunView (
32+
new ColorPicker
33+
{
34+
Title = "Pick a Color",
35+
BorderStyle = LineStyle.Single
36+
},
37+
cp => cp.SelectedColor);
3738

38-
MessageBox.Query ("Result", $"Selected color: {selectedColor}", "OK");
39+
MessageBox.Query (app, "Result", $"Selected color: {selectedColor}", "OK");
3940

4041
// Example 3: FlagSelector with typed enum result
41-
var flagSelector = new FlagSelector<SelectorStyles>
42+
FlagSelector<SelectorStyles> flagSelector = new()
4243
{
4344
Title = "Choose Styles",
4445
BorderStyle = LineStyle.Single
4546
};
4647

47-
var flagsRunnable = flagSelector.AsRunnable (fs => fs.Value);
48+
RunnableWrapper<FlagSelector<SelectorStyles>, SelectorStyles?> flagsRunnable = flagSelector.AsRunnable (fs => fs.Value);
4849
app.Run (flagsRunnable);
4950

50-
MessageBox.Query ("Result", $"Selected styles: {flagsRunnable.Result}", "OK");
51+
MessageBox.Query (app, "Result", $"Selected styles: {flagsRunnable.Result}", "OK");
5152
flagsRunnable.Dispose ();
5253

5354
// Example 4: Any View without result extraction
@@ -58,26 +59,28 @@
5859
Y = Pos.Center ()
5960
};
6061

61-
var labelRunnable = label.AsRunnable ();
62+
RunnableWrapper<Label, object> labelRunnable = label.AsRunnable ();
6263
app.Run (labelRunnable);
6364

6465
// Can still access the wrapped view
65-
MessageBox.Query ("Result", $"Label text was: {labelRunnable.WrappedView.Text}", "OK");
66+
MessageBox.Query (app, "Result", $"Label text was: {labelRunnable.WrappedView.Text}", "OK");
6667
labelRunnable.Dispose ();
6768

6869
// Example 5: Complex custom View made runnable
69-
var formView = CreateCustomForm ();
70-
var formRunnable = formView.AsRunnable (ExtractFormData);
70+
View formView = CreateCustomForm ();
71+
RunnableWrapper<View, FormData> formRunnable = formView.AsRunnable (ExtractFormData);
7172

7273
app.Run (formRunnable);
7374

7475
if (formRunnable.Result is { } formData)
7576
{
7677
MessageBox.Query (
77-
"Form Results",
78-
$"Name: {formData.Name}\nAge: {formData.Age}\nAgreed: {formData.Agreed}",
79-
"OK");
78+
app,
79+
"Form Results",
80+
$"Name: {formData.Name}\nAge: {formData.Age}\nAgreed: {formData.Agreed}",
81+
"OK");
8082
}
83+
8184
formRunnable.Dispose ();
8285

8386
app.Shutdown ();
@@ -126,10 +129,10 @@ View CreateCustomForm ()
126129
};
127130

128131
okButton.Accepting += (s, e) =>
129-
{
130-
form.App?.RequestStop ();
131-
e.Handled = true;
132-
};
132+
{
133+
form.App?.RequestStop ();
134+
e.Handled = true;
135+
};
133136

134137
form.Add (new Label { Text = "Name:", X = 2, Y = 1 });
135138
form.Add (nameField);
@@ -148,7 +151,7 @@ FormData ExtractFormData (View form)
148151
var ageField = form.SubViews.FirstOrDefault (v => v.Id == "ageField") as TextField;
149152
var agreeCheckbox = form.SubViews.FirstOrDefault (v => v.Id == "agreeCheckbox") as CheckBox;
150153

151-
return new FormData
154+
return new()
152155
{
153156
Name = nameField?.Text ?? string.Empty,
154157
Age = int.TryParse (ageField?.Text, out int age) ? age : 0,
@@ -157,7 +160,7 @@ FormData ExtractFormData (View form)
157160
}
158161

159162
// Result type for custom form
160-
record FormData
163+
internal record FormData
161164
{
162165
public string Name { get; init; } = string.Empty;
163166
public int Age { get; init; }

Examples/SelfContained/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,13 @@ public ExampleWindow ()
100100
{
101101
if (userNameText.Text == "admin" && passwordText.Text == "password")
102102
{
103-
MessageBox.Query ("Logging In", "Login Successful", "Ok");
103+
MessageBox.Query (App, "Logging In", "Login Successful", "Ok");
104104
UserName = userNameText.Text;
105105
Application.RequestStop ();
106106
}
107107
else
108108
{
109-
MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
109+
MessageBox.ErrorQuery (App, "Logging In", "Incorrect username or password", "Ok");
110110
}
111111
// When Accepting is handled, set e.Handled to true to prevent further processing.
112112
e.Handled = true;

Examples/UICatalog/Scenario.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ namespace UICatalog;
6767
/// };
6868
///
6969
/// var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
70-
/// button.Accept += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "Ok");
70+
/// button.Accept += (s, e) => MessageBox.ErrorQuery (App, "Error", "You pressed the button!", "Ok");
7171
/// appWindow.Add (button);
7272
///
7373
/// // Run - Start the application.
@@ -210,12 +210,12 @@ private void OnApplicationOnInitializedChanged (object? s, EventArgs<bool> a)
210210
void OnClearedContents (object? sender, EventArgs args) => BenchmarkResults.ClearedContentCount++;
211211
}
212212

213-
private void OnApplicationOnIteration (object? s, IterationEventArgs a)
213+
private void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a)
214214
{
215215
BenchmarkResults.IterationCount++;
216216
if (BenchmarkResults.IterationCount > BENCHMARK_MAX_NATURAL_ITERATIONS + (_demoKeys!.Count * BENCHMARK_KEY_PACING))
217217
{
218-
Application.RequestStop ();
218+
a.Value?.RequestStop ();
219219
}
220220
}
221221

Examples/UICatalog/Scenarios/Adornments.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public override void Main ()
1111
{
1212
Application.Init ();
1313

14-
Window app = new ()
14+
Window appWindow = new ()
1515
{
1616
Title = GetQuitKeyAndName (),
1717
BorderStyle = LineStyle.None
@@ -28,7 +28,7 @@ public override void Main ()
2828

2929
editor.Border!.Thickness = new (1, 2, 1, 1);
3030

31-
app.Add (editor);
31+
appWindow.Add (editor);
3232

3333
var window = new Window
3434
{
@@ -38,7 +38,7 @@ public override void Main ()
3838
Width = Dim.Fill (Dim.Func (_ => editor.Frame.Width)),
3939
Height = Dim.Fill ()
4040
};
41-
app.Add (window);
41+
appWindow.Add (window);
4242

4343
var tf1 = new TextField { Width = 10, Text = "TextField" };
4444
var color = new ColorPicker16 { Title = "BG", BoxHeight = 1, BoxWidth = 1, X = Pos.AnchorEnd () };
@@ -60,7 +60,7 @@ public override void Main ()
6060
var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
6161

6262
button.Accepting += (s, e) =>
63-
MessageBox.Query (20, 7, "Hi", $"Am I a {window.GetType ().Name}?", "Yes", "No");
63+
MessageBox.Query (appWindow.App, 20, 7, "Hi", $"Am I a {window.GetType ().Name}?", "Yes", "No");
6464

6565
var label = new TextView
6666
{
@@ -121,7 +121,7 @@ public override void Main ()
121121
Text = "text (Y = 1)",
122122
CanFocus = true
123123
};
124-
textFieldInPadding.Accepting += (s, e) => MessageBox.Query (20, 7, "TextField", textFieldInPadding.Text, "Ok");
124+
textFieldInPadding.Accepting += (s, e) => MessageBox.Query (appWindow.App, 20, 7, "TextField", textFieldInPadding.Text, "Ok");
125125
window.Padding.Add (textFieldInPadding);
126126

127127
var btnButtonInPadding = new Button
@@ -132,7 +132,7 @@ public override void Main ()
132132
CanFocus = true,
133133
HighlightStates = MouseState.None,
134134
};
135-
btnButtonInPadding.Accepting += (s, e) => MessageBox.Query (20, 7, "Hi", "Button in Padding Pressed!", "Ok");
135+
btnButtonInPadding.Accepting += (s, e) => MessageBox.Query (appWindow.App, 20, 7, "Hi", "Button in Padding Pressed!", "Ok");
136136
btnButtonInPadding.BorderStyle = LineStyle.Dashed;
137137
btnButtonInPadding.Border!.Thickness = new (1, 1, 1, 1);
138138
window.Padding.Add (btnButtonInPadding);
@@ -155,8 +155,8 @@ public override void Main ()
155155
editor.AutoSelectSuperView = window;
156156
editor.AutoSelectAdornments = true;
157157

158-
Application.Run (app);
159-
app.Dispose ();
158+
Application.Run (appWindow);
159+
appWindow.Dispose ();
160160

161161
Application.Shutdown ();
162162
}

Examples/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ private void OnWinOnInitialized (object? sender, EventArgs args)
7878
if (!f.Exists)
7979
{
8080
Debug.WriteLine ($"Could not find {f.FullName}");
81-
MessageBox.ErrorQuery ("Could not find gif", $"Could not find\n{f.FullName}", "Ok");
81+
MessageBox.ErrorQuery (_imageView?.App, "Could not find gif", $"Could not find\n{f.FullName}", "Ok");
8282

8383
return;
8484
}

0 commit comments

Comments
 (0)