This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4.9k
/
CancelKeyPress.Unix.cs
105 lines (89 loc) · 3.82 KB
/
CancelKeyPress.Unix.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
public partial class CancelKeyPressTests : RemoteExecutorTestBase
{
[PlatformSpecific(TestPlatforms.AnyUnix)] // Uses P/Invokes
public void HandlerInvokedForSigInt()
{
HandlerInvokedForSignal(SIGINT);
}
[PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.OSX)] // Jenkins blocks SIGQUIT on OS X, causing the test to fail in CI
public void HandlerInvokedForSigQuit()
{
HandlerInvokedForSignal(SIGQUIT);
}
[PlatformSpecific(TestPlatforms.AnyUnix)] // events are triggered by Unix signals (SIGINT, SIGQUIT, SIGCHLD).
public void ExitDetectionNotBlockedByHandler()
{
RemoteInvoke(() =>
{
var mre = new ManualResetEventSlim();
var tcs = new TaskCompletionSource<object>();
// CancelKeyPress is triggered by SIGINT/SIGQUIT
Console.CancelKeyPress += (sender, e) =>
{
tcs.SetResult(null);
// Block CancelKeyPress
Assert.True(mre.Wait(WaitFailTestTimeoutSeconds * 1000));
};
// Generate CancelKeyPress
Assert.Equal(0, kill(Process.GetCurrentProcess().Id, SIGINT));
// Wait till we block CancelKeyPress
Assert.True(tcs.Task.Wait(WaitFailTestTimeoutSeconds * 1000));
// Create a process and wait for it to exit.
using (RemoteInvokeHandle handle = RemoteInvoke(() => SuccessExitCode))
{
// Process exit is detected on SIGCHLD
Assert.Equal(SuccessExitCode, handle.ExitCode);
}
// Release CancelKeyPress
mre.Set();
return SuccessExitCode;
}).Dispose();
}
private void HandlerInvokedForSignal(int signalOuter)
{
// On Windows we could use GenerateConsoleCtrlEvent to send a ctrl-C to the process,
// however that'll apply to all processes associated with the same group, which will
// include processes like the code coverage tool when doing code coverage runs, causing
// those other processes to exit. As such, we test this only on Unix, where we can
// send a SIGINT signal to this specific process only.
// This test sends a SIGINT back to itself... if run in the xunit process, this would end
// up canceling the rest of xunit's tests. So we run the test itself in a separate process.
RemoteInvoke(signalStr =>
{
var tcs = new TaskCompletionSource<ConsoleSpecialKey>();
ConsoleCancelEventHandler handler = (sender, e) =>
{
e.Cancel = true;
tcs.SetResult(e.SpecialKey);
};
Console.CancelKeyPress += handler;
try
{
int signalInner = int.Parse(signalStr);
Assert.Equal(0, kill(Process.GetCurrentProcess().Id, signalInner));
Assert.True(tcs.Task.Wait(WaitFailTestTimeoutSeconds * 1000));
Assert.Equal(
signalInner == SIGINT ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak,
tcs.Task.Result);
}
finally
{
Console.CancelKeyPress -= handler;
}
return SuccessExitCode;
}, signalOuter.ToString()).Dispose();
}
[DllImport("libc", SetLastError = true)]
private static extern int kill(int pid, int sig);
private const int SIGINT = 2;
private const int SIGQUIT = 3;
}