Add Get-Uptime to Microsoft.PowerShell.Utility (C# cmdlet) (#2517)

* Add Get-Uptime (C# code)

1. Add Get-Uptime cmdlet (C# code)
2. Modify psd1 files
3. Add tests

* Remove Pretty parameter after Powershell committee conclusion

1. Remove Pretty parameter
2. Remove tests for Pretty
3. Add Throw if  IsHighResolution is False
4. Fix formatting

* Add test to check throw

1. Add hook that simulate 'System.Diagnostics.Stopwatch.IsHighResolution == false' to test Get-Uptime throw
2. Add test to check throw

* Fixes after review

1. Remove Timespan parameter
2. Remove unneeded tests
3. Fix localization
4. Add string consts
5. Fix throw

* Fix OutputType to add parameter set name information
This commit is contained in:
iSazonov
2016-11-10 09:51:29 -08:00
committed by Dongbo Wang
parent 2077e42b52
commit b56bbeabc6
7 changed files with 247 additions and 3 deletions
@@ -0,0 +1,72 @@
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System;
using System.Management.Automation;
using System.Diagnostics;
using System.Management.Automation.Internal;
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// This class implements Get-Uptime
/// </summary>
[Cmdlet(VerbsCommon.Get, "Uptime", DefaultParameterSetName = TimespanParameterSet, HelpUri = "")]
[OutputType(typeof(TimeSpan), ParameterSetName = new string[] { TimespanParameterSet })]
[OutputType(typeof(DateTime), ParameterSetName = new string[] { SinceParameterSet })]
public class GetUptimeCommand : PSCmdlet
{
/// <summary>
/// Since parameter
/// The system startup time
/// </summary>
/// <value></value>
[Parameter(ParameterSetName = SinceParameterSet)]
public SwitchParameter Since { get; set; } = new SwitchParameter();
/// <summary>
/// ProcessRecord() override
/// This is the main entry point for the cmdlet
/// </summary>
protected override void ProcessRecord()
{
// Get-Uptime throw if IsHighResolution = false
// because stopwatch.GetTimestamp() return DateTime.UtcNow.Ticks
// instead of ticks from system startup.
// InternalTestHooks.StopwatchIsNotHighResolution is used as test hook.
if (Stopwatch.IsHighResolution && !InternalTestHooks.StopwatchIsNotHighResolution)
{
TimeSpan uptime = TimeSpan.FromSeconds(Stopwatch.GetTimestamp()/Stopwatch.Frequency);
switch (ParameterSetName)
{
case TimespanParameterSet:
// return TimeSpan of time since the system started up
WriteObject(uptime);
break;
case SinceParameterSet:
// return Datetime when the system started up
WriteObject(DateTime.Now.Subtract(uptime));
break;
}
}
else
{
WriteDebug("System.Diagnostics.Stopwatch.IsHighResolution returns 'False'.");
Exception exc = new NotSupportedException(GetUptimeStrings.GetUptimePlatformIsNotSupported);
ThrowTerminatingError(new ErrorRecord(exc, "GetUptimePlatformIsNotSupported", ErrorCategory.NotImplemented, null));
}
}
/// <summary>
/// Parameter set name for Timespan OutputType.
/// </summary>
private const string TimespanParameterSet = "Timespan";
/// <summary>
/// Parameter set name for DateTime OutputType.
/// </summary>
private const string SinceParameterSet = "Since";
}
}
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="GetUptimePlatformIsNotSupported" xml:space="preserve">
<value>"The platform is not supported (System.Diagnostics.Stopwatch.IsHighResolution is false)."</value>
</data>
</root>
@@ -22,7 +22,7 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide",
"Get-PSBreakpoint", "Remove-PSBreakpoint", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack",
"Get-TraceSource", "Set-TraceSource", "Trace-Command",
"Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug",
"Get-RunspaceDebug", "Wait-Debugger"
"Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime"
FunctionsToExport= "Get-FileHash", "New-TemporaryFile", "New-Guid", "Format-Hex", "Import-PowerShellDataFile"
AliasesToExport= "fhx"
NestedModules="Microsoft.PowerShell.Commands.Utility.dll","Microsoft.PowerShell.Utility.psm1"
@@ -22,7 +22,7 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide",
"Get-PSBreakpoint", "Remove-PSBreakpoint", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack",
"Get-TraceSource", "Set-TraceSource", "Trace-Command",
"Unblock-File", "Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug",
"Get-RunspaceDebug", "Wait-Debugger"
"Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime"
FunctionsToExport= "Get-FileHash", "New-TemporaryFile", "New-Guid", "Format-Hex", "Import-PowerShellDataFile",
"ConvertFrom-SddlString"
AliasesToExport= "fhx"
@@ -24,7 +24,7 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide",
"Remove-PSBreakpoint", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack",
"Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Show-Command", "Unblock-File",
"Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug", "Get-RunspaceDebug", "Wait-Debugger",
"ConvertFrom-String", "Convert-String"
"ConvertFrom-String", "Convert-String" , "Get-Uptime"
FunctionsToExport= "Get-FileHash", "New-TemporaryFile", "New-Guid", "Format-Hex", "Import-PowerShellDataFile",
"ConvertFrom-SddlString"
AliasesToExport= "CFS", "fhx"
@@ -1577,6 +1577,8 @@ namespace System.Management.Automation.Internal
internal static bool BypassOnlineHelpRetrieval;
// It's useful to test that we don't depend on the ScriptBlock and AST objects and can use a re-parsed version.
internal static bool IgnoreScriptBlockCache;
// Simulate 'System.Diagnostics.Stopwatch.IsHighResolution is false' to test Get-Uptime throw
internal static bool StopwatchIsNotHighResolution;
/// <summary>This member is used for internal test purposes.</summary>
public static void SetTestHook(string property, bool value)
@@ -0,0 +1,47 @@
Describe "Get-Uptime" -Tags "CI" {
BeforeAll {
$IsHighResolution = [system.diagnostics.stopwatch]::IsHighResolution
# Skip Get-Uptime test if IsHighResolution = false
# because stopwatch.GetTimestamp() return DateTime.UtcNow.Ticks
# instead of ticks from system startup
if ( ! $IsHighResolution )
{
$origDefaults = $PSDefaultParameterValues.Clone()
$PSDefaultParameterValues['it:skip'] = $true
}
}
AfterAll {
if ( ! $IsHighResolution ){
$global:PSDefaultParameterValues = $origDefaults
}
}
It "Get-Uptime return timespan (default -Timespan)" {
$upt = Get-Uptime
$upt | Should Not Be $null
($upt).Gettype().Name | Should Be "Timespan"
}
It "Get-Uptime -Since return DateTime" {
$upt = Get-Uptime -Since
$upt | Should Not Be $null
($upt).Gettype().Name | Should Be "DateTime"
}
It "Get-Uptime throw if IsHighResolution == false" {
try
{
# Enable the test hook
[system.management.automation.internal.internaltesthooks]::SetTestHook('StopwatchIsNotHighResolution', $true)
Get-Uptime
throw "No Exception!"
}
catch
{
$_.FullyQualifiedErrorId | Should be "GetUptimePlatformIsNotSupported,Microsoft.PowerShell.Commands.GetUptimeCommand"
}
finally
{
# Disable the test hook
[system.management.automation.internal.internaltesthooks]::SetTestHook('StopwatchIsHighResolutionIsFalse', $false)
}
}
}