2019-10-17 13:30:18 -04:00
|
|
|
|
using NUnit.Framework;
|
|
|
|
|
using NUnit.Framework.Internal;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using Vanara.Extensions;
|
|
|
|
|
using Vanara.InteropServices;
|
|
|
|
|
using Vanara.PInvoke;
|
|
|
|
|
using Vanara.PInvoke.Tests;
|
|
|
|
|
using static Vanara.PInvoke.Kernel32;
|
|
|
|
|
|
|
|
|
|
namespace Vanara.Diagnostics.Tests
|
|
|
|
|
{
|
|
|
|
|
[TestFixture]
|
|
|
|
|
public class JobTests
|
|
|
|
|
{
|
|
|
|
|
[Test]
|
|
|
|
|
public void ActiveProcessLimitTest()
|
|
|
|
|
{
|
|
|
|
|
using var job = Job.Create();
|
|
|
|
|
|
|
|
|
|
job.RuntimeLimits.ActiveProcessLimit = 2;
|
|
|
|
|
Assert.That(job.RuntimeLimits.ActiveProcessLimit.Value, Is.EqualTo(2U));
|
|
|
|
|
job.RuntimeLimits.ActiveProcessLimit = 0;
|
|
|
|
|
Assert.That(job.RuntimeLimits.ActiveProcessLimit.Value, Is.EqualTo(0U));
|
|
|
|
|
Assert.That(() => job.StartProcess("notepad.exe"), Throws.Exception);
|
|
|
|
|
job.RuntimeLimits.ActiveProcessLimit = null;
|
|
|
|
|
Assert.That(job.RuntimeLimits.ActiveProcessLimit.HasValue, Is.False);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void EventsTest()
|
|
|
|
|
{
|
|
|
|
|
using var job = Job.Create(Environment.UserName);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
job.NewProcess += msgHndlr;
|
|
|
|
|
job.ProcessExited += msgHndlr;
|
|
|
|
|
job.JobMemoryLimitExceeded += msgHndlr;
|
|
|
|
|
job.JobNotificationLimitExceeded += notHndlr;
|
|
|
|
|
|
|
|
|
|
var startInfo = new ProcessStartInfo("notepad.exe") { UseShellExecute = false };
|
|
|
|
|
var p1 = new Process { StartInfo = startInfo };
|
|
|
|
|
p1.StartEx(CREATE_PROCESS.CREATE_SUSPENDED);
|
|
|
|
|
job.AssignProcess(p1);
|
|
|
|
|
p1.ResumePrimaryThread();
|
|
|
|
|
|
|
|
|
|
Thread.Sleep(200);
|
|
|
|
|
|
|
|
|
|
job.Notifications.JobMemoryLimit = job.Statistics.PeakJobMemoryUsed;
|
|
|
|
|
job.RuntimeLimits.JobMemoryLimit = job.Notifications.JobMemoryLimit * 3;
|
|
|
|
|
//TestContext.WriteLine($"JobMemory: NotLim: {job.JobMemory.NotificationLimit}, Lim: {job.JobMemory.Limit}");
|
|
|
|
|
|
|
|
|
|
//job.PerJobUserTime.NotificationLimit = job.Statistics.TotalUserTime;
|
|
|
|
|
//job.PerJobUserTime.Limit = TimeSpan.FromTicks(job.PerJobUserTime.NotificationLimit.Value.Ticks * 3);
|
|
|
|
|
//TestContext.WriteLine($"PerJobUserTime: NotLim: {job.PerJobUserTime.NotificationLimit}, Lim: {job.PerJobUserTime.Limit}");
|
|
|
|
|
|
|
|
|
|
var p2 = new Process { StartInfo = startInfo };
|
|
|
|
|
p2.StartEx(CREATE_PROCESS.CREATE_SUSPENDED);
|
|
|
|
|
job.AssignProcess(p2);
|
|
|
|
|
p2.ResumePrimaryThread();
|
|
|
|
|
|
|
|
|
|
Thread.Sleep(2000);
|
|
|
|
|
Thread.Yield();
|
|
|
|
|
|
|
|
|
|
p1.Kill();
|
|
|
|
|
Thread.Sleep(200);
|
|
|
|
|
Thread.Yield();
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
job.TerminateAllProcesses(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void msgHndlr(object s, JobEventArgs e) => TestContext.WriteLine($"{DateTime.Now:u}: {e.JobMessage}, {e.ProcessId}");
|
|
|
|
|
static void notHndlr(object s, JobNotificationEventArgs e) => TestContext.WriteLine($"{DateTime.Now:u}: {e.JobMessage}, {e.Limit}, Limit: {e.NotificationLimit}, Val: {e.ReportedValue}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void LimitsTest()
|
|
|
|
|
{
|
|
|
|
|
using var job = Job.Create();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Assert.That(job.Processes.Count, Is.EqualTo(0));
|
|
|
|
|
var p1 = TestHelper.RunThrottleApp();
|
|
|
|
|
job.AssignProcess(p1);
|
|
|
|
|
Thread.Sleep(200);
|
|
|
|
|
|
|
|
|
|
job.RuntimeLimits.ActiveProcessLimit = 1;
|
|
|
|
|
Assert.That(() => job.StartProcess("notepad.exe"), Throws.Exception);
|
|
|
|
|
job.RuntimeLimits.ActiveProcessLimit = null;
|
|
|
|
|
|
|
|
|
|
Test(job.RuntimeLimits, 1.0, (s, v) => s.CpuRateLimit = v, s => s.CpuRateLimit, true);
|
|
|
|
|
Test(job.RuntimeLimits, (25.0, 75.0), (s, v) => s.CpuRatePortion = v, s => s.CpuRatePortion, true);
|
|
|
|
|
Test(job.RuntimeLimits, 3, (s, v) => s.CpuRateRelativeWeight = v, s => s.CpuRateRelativeWeight, true);
|
|
|
|
|
Test(job.RuntimeLimits, 4096UL, (s, v) => s.JobMemoryLimit = v, s => s.JobMemoryLimit, true);
|
|
|
|
|
Test(job.RuntimeLimits, 4096UL, (s, v) => s.MaxBandwidth = v, s => s.MaxBandwidth);
|
|
|
|
|
TestGT(job.RuntimeLimits, TimeSpan.FromTicks(30000), (s, v) => s.PerJobUserTimeLimit = v, s => s.PerJobUserTimeLimit);
|
|
|
|
|
TestGT(job.RuntimeLimits, TimeSpan.FromTicks(30000), (s, v) => s.PerProcessUserTimeLimit = v, s => s.PerProcessUserTimeLimit);
|
|
|
|
|
Test(job.RuntimeLimits, 4096UL, (s, v) => s.ProcessMemoryLimit = v, s => s.ProcessMemoryLimit, true);
|
|
|
|
|
var pmc = PROCESS_MEMORY_COUNTERS.Default;
|
|
|
|
|
GetProcessMemoryInfo(p1, out pmc, pmc.cb);
|
|
|
|
|
Test(job.RuntimeLimits, (pmc.WorkingSetSize, pmc.PeakWorkingSetSize), (s, v) => s.WorkingSetSize = v, s => s.WorkingSetSize, true);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
job.TerminateAllProcesses(0);
|
|
|
|
|
Thread.Sleep(200);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TestGT<T, TS>(TS js, T? value, Action<TS, T?> set, Func<TS, T?> get) where T : struct
|
|
|
|
|
{
|
|
|
|
|
Assert.That(() => set(js, value), Throws.Nothing);
|
|
|
|
|
Assert.That(get(js).Value, Is.GreaterThanOrEqualTo(value));
|
|
|
|
|
|
|
|
|
|
Assert.That(() => set(js, null), Throws.Nothing);
|
|
|
|
|
Assert.That(get(js), Is.Null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void NotificationTest()
|
|
|
|
|
{
|
|
|
|
|
using var job = Job.Create();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
job.NewProcess += (s, e) => TestContext.WriteLine($"{DateTime.Now:u}: {e.JobMessage}, {e.ProcessId}");
|
|
|
|
|
job.JobNotificationLimitExceeded += notHndlr;
|
|
|
|
|
|
|
|
|
|
job.Notifications.IoRateControlTolerance = JOBOBJECT_RATE_CONTROL_TOLERANCE.ToleranceLow;
|
|
|
|
|
job.Notifications.IoRateControlToleranceInterval = JOBOBJECT_RATE_CONTROL_TOLERANCE_INTERVAL.ToleranceIntervalShort;
|
|
|
|
|
//job.Notifications.IoReadBytesLimit = 1;
|
|
|
|
|
//job.Notifications.IoWriteBytesLimit = 1;
|
|
|
|
|
job.Notifications.JobMemoryLimit = 8092;
|
|
|
|
|
job.Notifications.JobLowMemoryLimit = 4096;
|
|
|
|
|
job.Notifications.NetRateControlTolerance = JOBOBJECT_RATE_CONTROL_TOLERANCE.ToleranceLow;
|
|
|
|
|
job.Notifications.NetRateControlToleranceInterval = JOBOBJECT_RATE_CONTROL_TOLERANCE_INTERVAL.ToleranceIntervalShort;
|
|
|
|
|
//job.Notifications.PerJobUserTimeLimit = TimeSpan.FromMilliseconds(1);
|
|
|
|
|
//job.Notifications.RateControlTolerance = JOBOBJECT_RATE_CONTROL_TOLERANCE.ToleranceLow;
|
|
|
|
|
//job.Notifications.RateControlToleranceInterval = JOBOBJECT_RATE_CONTROL_TOLERANCE_INTERVAL.ToleranceIntervalShort;
|
|
|
|
|
|
|
|
|
|
job.StartProcess("notepad.exe");
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
|
|
|
{
|
|
|
|
|
Thread.Sleep(1000);
|
|
|
|
|
Thread.Yield();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
job.TerminateAllProcesses(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void notHndlr(object s, JobNotificationEventArgs e) => TestContext.WriteLine($"{DateTime.Now:u}: {e.JobMessage}, {e.Limit}, Limit: {e.NotificationLimit}, Val: {e.ReportedValue}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void OpenJobTest()
|
|
|
|
|
{
|
|
|
|
|
using var job = Job.Create(Environment.UserName);
|
|
|
|
|
Assert.That(job.Handle, ResultIs.ValidHandle);
|
|
|
|
|
//using var job1 = Job.Open(Environment.UserName, JobAccessRight.JOB_OBJECT_QUERY);
|
|
|
|
|
//Assert.That(job1.Handle, ResultIs.ValidHandle);
|
|
|
|
|
|
|
|
|
|
Assert.That(() => job.Settings.GroupAffinity, Throws.Nothing);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void ProcessesTest()
|
|
|
|
|
{
|
|
|
|
|
Process p2 = null;
|
|
|
|
|
using (var job = Job.Create())
|
|
|
|
|
{
|
|
|
|
|
job.Settings.KillOnJobClose = true;
|
|
|
|
|
|
|
|
|
|
var curProc = Process.GetCurrentProcess();
|
|
|
|
|
Assert.That(job.ContainsProcess(curProc), Is.False);
|
|
|
|
|
|
|
|
|
|
var p1 = Process.Start("notepad.exe");
|
|
|
|
|
job.AssignProcess(p1);
|
|
|
|
|
job.AssignProcess(p2 = Process.Start("notepad.exe"));
|
|
|
|
|
|
|
|
|
|
Assert.That(job.Processes.Count, Is.EqualTo(2));
|
|
|
|
|
Assert.That(job.Processes.Count(), Is.EqualTo(2));
|
|
|
|
|
Assert.That(job.Processes.First().Id, Is.EqualTo(p1.Id));
|
|
|
|
|
|
|
|
|
|
p1.Kill();
|
|
|
|
|
Assert.That(p1.WaitForExit(500), Is.True);
|
|
|
|
|
Assert.That(job.Processes.Count, Is.EqualTo(1));
|
|
|
|
|
Assert.That(job.Processes.Count(), Is.EqualTo(1));
|
|
|
|
|
}
|
|
|
|
|
Assert.That(p2.WaitForExit(500), Is.True);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 13:08:06 -05:00
|
|
|
|
[Test]
|
|
|
|
|
public void SettingsMixTest()
|
|
|
|
|
{
|
|
|
|
|
using var job = Job.Create();
|
|
|
|
|
Assert.That(() => job.Settings.Affinity = (UIntPtr)0x2, Throws.Nothing);
|
|
|
|
|
job.Settings.KillOnJobClose = true;
|
|
|
|
|
Assert.That(() => job.Settings.Affinity = (UIntPtr)0x2, Throws.Nothing);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-17 13:30:18 -04:00
|
|
|
|
[Test]
|
|
|
|
|
public void SettingsTest()
|
|
|
|
|
{
|
|
|
|
|
using var job = Job.Create();
|
|
|
|
|
|
|
|
|
|
Test(job.Settings, new UIntPtr(0xf), (s, v) => s.Affinity = v, s => s.Affinity, true);
|
|
|
|
|
TestBool(job.Settings, (s, v) => s.ChildProcessBreakawayAllowed = v, s => s.ChildProcessBreakawayAllowed);
|
|
|
|
|
TestBool(job.Settings, (s, v) => s.ChildProcessSilentBreakawayAllowed = v, s => s.ChildProcessSilentBreakawayAllowed);
|
|
|
|
|
TestBool(job.Settings, (s, v) => s.DieOnUnhandledException = v, s => s.DieOnUnhandledException);
|
|
|
|
|
Test(job.Settings, 0xf, (s, v) => s.DscpTag = v, s => s.DscpTag);
|
|
|
|
|
TestBool(job.Settings, (s, v) => s.KillOnJobClose = v, s => s.KillOnJobClose);
|
|
|
|
|
Test(job.Settings, ProcessPriorityClass.BelowNormal, (s, v) => s.PriorityClass = v, s => s.PriorityClass, true);
|
|
|
|
|
Test(job.Settings, 3, (s, v) => s.SchedulingClass = v, s => s.SchedulingClass);
|
|
|
|
|
TestBool(job.Settings, (s, v) => s.TerminateProcessesAtEndOfJobTimeLimit = v, s => s.TerminateProcessesAtEndOfJobTimeLimit);
|
|
|
|
|
Assert.That(() => job.Settings.UIRestrictionsClass = JOBOBJECT_UILIMIT_FLAGS.JOB_OBJECT_UILIMIT_ALL, Throws.Nothing);
|
|
|
|
|
Assert.That(job.Settings.UIRestrictionsClass, Is.EqualTo(JOBOBJECT_UILIMIT_FLAGS.JOB_OBJECT_UILIMIT_ALL));
|
|
|
|
|
|
|
|
|
|
Assert.That(() => job.Settings.GroupAffinity.First(), Throws.Nothing);
|
|
|
|
|
Assert.That(job.Settings.GroupAffinity.First().Mask.ToUInt32(), Is.Not.Zero);
|
|
|
|
|
Assert.That(() => job.Settings.GroupAffinity = job.Settings.GroupAffinity, Throws.Nothing);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Test<T, TS>(TS js, T? value, Action<TS, T?> set, Func<TS, T?> get, bool ignDef = false) where T : struct
|
|
|
|
|
{
|
|
|
|
|
Assert.That(() => set(js, value), Throws.Nothing);
|
|
|
|
|
Assert.That(get(js).Value, Is.EqualTo(value));
|
|
|
|
|
|
|
|
|
|
if (!ignDef)
|
|
|
|
|
{
|
|
|
|
|
Assert.That(() => set(js, default(T)), Throws.Nothing);
|
|
|
|
|
Assert.That(get(js).Value, Is.EqualTo(default(T)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.That(() => set(js, null), Throws.Nothing);
|
|
|
|
|
Assert.That(get(js), Is.Null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void TestBool<TS>(TS js, Action<TS, bool> set, Func<TS, bool> get)
|
|
|
|
|
{
|
|
|
|
|
var orig = get(js);
|
|
|
|
|
|
|
|
|
|
Assert.That(() => set(js, !orig), Throws.Nothing);
|
|
|
|
|
Assert.That(get(js), Is.EqualTo(!orig));
|
|
|
|
|
|
|
|
|
|
Assert.That(() => set(js, orig), Throws.Nothing);
|
|
|
|
|
Assert.That(get(js), Is.EqualTo(orig));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void StatsTest()
|
|
|
|
|
{
|
|
|
|
|
using var job = Job.Create();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
job.AssignProcess(Process.Start("notepad.exe"));
|
|
|
|
|
Thread.Sleep(200);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
job.TerminateAllProcesses(0);
|
|
|
|
|
Thread.Sleep(200);
|
|
|
|
|
}
|
|
|
|
|
var stats = job.Statistics;
|
|
|
|
|
stats.WriteValues();
|
|
|
|
|
Assert.That(stats.PeakJobMemoryUsed, Is.GreaterThan(0UL));
|
|
|
|
|
Assert.That(stats.PeakProcessMemoryUsed, Is.GreaterThan(0UL));
|
|
|
|
|
Assert.That(stats.ThisPeriodTotalKernelTime.Ticks, Is.GreaterThan(0UL));
|
|
|
|
|
Assert.That(stats.ThisPeriodTotalUserTime.Ticks, Is.GreaterThanOrEqualTo(0UL));
|
|
|
|
|
Assert.That(stats.TotalKernelTime.Ticks, Is.GreaterThan(0UL));
|
|
|
|
|
Assert.That(stats.TotalPageFaultCount, Is.GreaterThanOrEqualTo(0UL));
|
|
|
|
|
Assert.That(stats.TotalProcesses, Is.GreaterThan(0UL));
|
|
|
|
|
Assert.That(stats.TotalTerminatedProcesses, Is.GreaterThanOrEqualTo(0UL));
|
|
|
|
|
Assert.That(stats.TotalUserTime.Ticks, Is.GreaterThanOrEqualTo(0UL));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void TempTest()
|
|
|
|
|
{
|
|
|
|
|
using var job = Job.Create();
|
|
|
|
|
var str = new JOBOBJECT_NOTIFICATION_LIMIT_INFORMATION_2 { LimitFlags = JOBOBJECT_LIMIT_FLAGS.JOB_OBJECT_LIMIT_JOB_MEMORY_LOW, JobLowMemoryLimit = 4096 };
|
|
|
|
|
using var mem = SafeHGlobalHandle.CreateFromStructure(str);
|
|
|
|
|
if (!SetInformationJobObject(job, JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation2, mem, mem.Size))
|
|
|
|
|
TestContext.WriteLine($"{Win32Error.GetLastError()}");
|
|
|
|
|
|
|
|
|
|
mem.Zero();
|
|
|
|
|
if (QueryInformationJobObject(job, JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation2, mem, mem.Size, out _))
|
|
|
|
|
mem.ToStructure<JOBOBJECT_NOTIFICATION_LIMIT_INFORMATION_2>().WriteValues();
|
|
|
|
|
|
|
|
|
|
str.LimitFlags = 0;
|
|
|
|
|
str.JobLowMemoryLimit = 0;
|
|
|
|
|
mem.Write(str);
|
|
|
|
|
if (!SetInformationJobObject(job, JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation2, mem, mem.Size))
|
|
|
|
|
TestContext.WriteLine($"{Win32Error.GetLastError()}");
|
|
|
|
|
|
|
|
|
|
mem.Zero();
|
|
|
|
|
if (QueryInformationJobObject(job, JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation2, mem, mem.Size, out _))
|
|
|
|
|
mem.ToStructure<JOBOBJECT_NOTIFICATION_LIMIT_INFORMATION_2>().WriteValues();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|