Bug fixes and cleanup in preparation for push.

This commit is contained in:
Mike Richmond committed 2016-08-04 13:15:34 -07:00
1 parent e6e785ec92
commit f8b9b88986
3 files changed
+128 -248

No files matched your search

+1 -1
View File
@@ -537,7 +537,7 @@ function Start-PSPester {
Write-Verbose "Import-Module '$moduleDir'; Invoke-Pester $tagString $Path" Write-Verbose "Import-Module '$moduleDir'; Invoke-Pester $tagString $Path"
$powershellexe = get-psoutput $powershellexe = get-psoutput
& $powershell -noprofile -c "Import-Module '$moduleDir'; Invoke-Pester $tagString $Path" & $powershell -noprofile -c "Set-ExecutionPolicy Unrestricted; Import-Module '$moduleDir'; Invoke-Pester $tagString $Path"
if ($LASTEXITCODE -ne 0) { if ($LASTEXITCODE -ne 0) {
throw "$LASTEXITCODE Pester tests failed" throw "$LASTEXITCODE Pester tests failed"
} }
@@ -70,7 +70,7 @@ namespace System.Management.Automation
/// <param name="shellId">The shell associated with this policy. Typically, it is "Microsoft.PowerShell"</param> /// <param name="shellId">The shell associated with this policy. Typically, it is "Microsoft.PowerShell"</param>
/// <returns></returns> /// <returns></returns>
internal abstract string GetMachineExecutionPolicy(PropertyScope scope, string shellId); internal abstract string GetMachineExecutionPolicy(PropertyScope scope, string shellId);
internal abstract void RemoveMachineExecutionPolicy(PropertyScope scope, string shellId); // TODO: Necessary? internal abstract void RemoveMachineExecutionPolicy(PropertyScope scope, string shellId);
internal abstract void SetMachineExecutionPolicy(PropertyScope scope, string shellId, string executionPolicy); internal abstract void SetMachineExecutionPolicy(PropertyScope scope, string shellId, string executionPolicy);
/// <summary> /// <summary>
@@ -111,7 +111,6 @@ namespace System.Management.Automation
{ {
/// <summary> /// <summary>
/// Template method to generate the appropriate accessor for a given environment. /// Template method to generate the appropriate accessor for a given environment.
/// TODO: Is it really necessary to make this Singleton-ish?
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
internal static PropertyAccessor GetPropertyAccessor() internal static PropertyAccessor GetPropertyAccessor()
@@ -119,7 +118,7 @@ namespace System.Management.Automation
if (null == _activePropertyAccessor) if (null == _activePropertyAccessor)
{ {
#if CORECLR #if CORECLR
// TODO: I must differentiate between inbox PS and side-by-side PS here! // TODO: I must differentiate between inbox PS and side-by-side PS here eventually
_activePropertyAccessor = new JsonConfigFileAccessor(); _activePropertyAccessor = new JsonConfigFileAccessor();
#else #else
_activePropertyAccessor = new RegistryAccessor(); _activePropertyAccessor = new RegistryAccessor();
@@ -166,9 +165,7 @@ namespace System.Management.Automation
// //
// Initialize (and create if necessary) the per-user configuration directory // Initialize (and create if necessary) the per-user configuration directory
// //
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); appDataConfigDirectory = Path.Combine(Utils.GetUserSettingsDirectory(), configDirectoryName);
string psConfigPath = Path.Combine("PowerShell", "1.0", configDirectoryName);
appDataConfigDirectory = Path.Combine(appDataPath, psConfigPath);
if (!Directory.Exists(appDataConfigDirectory)) if (!Directory.Exists(appDataConfigDirectory))
{ {
@@ -183,9 +180,9 @@ namespace System.Management.Automation
/// <returns></returns> /// <returns></returns>
internal override string GetModulePath(ModulePathTarget target) internal override string GetModulePath(ModulePathTarget target)
{ {
// TODO: This feels unnecessary since it can be calculated based on the current user...
if (ModulePathTarget.CurrentUser == target) if (ModulePathTarget.CurrentUser == target)
{ {
// TODO: This feels unnecessary since it can be calculated based on the current user...
return ReadValueFromFile<string>(modulePathFileName, "UserModulePath"); return ReadValueFromFile<string>(modulePathFileName, "UserModulePath");
} }
else if (ModulePathTarget.SharedCommon == target) else if (ModulePathTarget.SharedCommon == target)
@@ -194,8 +191,8 @@ namespace System.Management.Automation
} }
else else
{ {
// TODO: Better way to handle this? // TODO: Is there a better way to handle this?
throw new InvalidEnumArgumentException(); throw new ArgumentException();
} }
} }
@@ -212,10 +209,10 @@ namespace System.Management.Automation
/// </summary> /// </summary>
/// <param name="scope">Whether this is a system-wide or per-user setting.</param> /// <param name="scope">Whether this is a system-wide or per-user setting.</param>
/// <param name="shellId">The shell associated with this policy. Typically, it is "Microsoft.PowerShell"</param> /// <param name="shellId">The shell associated with this policy. Typically, it is "Microsoft.PowerShell"</param>
/// <returns></returns> /// <returns>The execution policy if found. Null otherwise.</returns>
internal override string GetMachineExecutionPolicy(PropertyScope scope, string shellId) internal override string GetMachineExecutionPolicy(PropertyScope scope, string shellId)
{ {
string execPolicy = "Undefined"; string execPolicy = null;
string scopeDirectory = psHomeConfigDirectory; string scopeDirectory = psHomeConfigDirectory;
// Defaults to system wide. // Defaults to system wide.
@@ -232,13 +229,22 @@ namespace System.Management.Automation
{ {
execPolicy = rawExecPolicy; execPolicy = rawExecPolicy;
} }
// TODO: Throw if NullOrEmpty?
return execPolicy; return execPolicy;
} }
internal override void RemoveMachineExecutionPolicy(PropertyScope scope, string shellId) // TODO: Necessary? internal override void RemoveMachineExecutionPolicy(PropertyScope scope, string shellId)
{ {
// TODO: Work to do here if I decide to support it string scopeDirectory = psHomeConfigDirectory;
// Defaults to system wide.
if (PropertyScope.CurrentUser == scope)
{
scopeDirectory = appDataConfigDirectory;
}
string fileName = Path.Combine(scopeDirectory, execPolicyFileName);
RemoveValueFromFile<string>(fileName, shellId);
} }
internal override void SetMachineExecutionPolicy(PropertyScope scope, string shellId, string executionPolicy) internal override void SetMachineExecutionPolicy(PropertyScope scope, string shellId, string executionPolicy)
@@ -295,7 +301,7 @@ namespace System.Management.Automation
/// "ConsolePrompting" : bool /// "ConsolePrompting" : bool
/// } /// }
/// </summary> /// </summary>
/// <returns>Whether console prompting should happen.</returns> /// <returns>Whether console prompting should happen. If the value cannot be read it defaults to false.</returns>
internal override bool GetConsolePrompting(ref Exception exception) internal override bool GetConsolePrompting(ref Exception exception)
{ {
string fileName = Path.Combine(psHomeConfigDirectory, consolePromptingFileName); string fileName = Path.Combine(psHomeConfigDirectory, consolePromptingFileName);
@@ -317,7 +323,7 @@ namespace System.Management.Automation
/// "DisablePromptToUpdateHelp" : bool /// "DisablePromptToUpdateHelp" : bool
/// } /// }
/// </summary> /// </summary>
/// <returns>Boolean indicating whether Update-Help should prompt</returns> /// <returns>Boolean indicating whether Update-Help should prompt. If the value cannot be read, it defaults to false.</returns>
internal override bool GetDisablePromptToUpdateHelp() internal override bool GetDisablePromptToUpdateHelp()
{ {
string fileName = Path.Combine(psHomeConfigDirectory, updateHelpPromptFileName); string fileName = Path.Combine(psHomeConfigDirectory, updateHelpPromptFileName);
@@ -339,7 +345,7 @@ namespace System.Management.Automation
/// "DefaultSourcePath" : "path to local updatable help location" /// "DefaultSourcePath" : "path to local updatable help location"
/// } /// }
/// </summary> /// </summary>
/// <returns></returns> /// <returns>The source path if found, null otherwise.</returns>
internal override string GetDefaultSourcePath() internal override string GetDefaultSourcePath()
{ {
string fileName = Path.Combine(psHomeConfigDirectory, updatableHelpSourcePathFileName); string fileName = Path.Combine(psHomeConfigDirectory, updatableHelpSourcePathFileName);
@@ -350,8 +356,7 @@ namespace System.Management.Automation
{ {
return rawExecPolicy; return rawExecPolicy;
} }
// TODO: Throw if NullOrEmpty? return null;
return String.Empty;
} }
internal override void SetDefaultSourcePath(string defaultPath) internal override void SetDefaultSourcePath(string defaultPath)
@@ -370,7 +375,11 @@ namespace System.Management.Automation
using (JsonTextReader jsonReader = new JsonTextReader(streamRdr)) using (JsonTextReader jsonReader = new JsonTextReader(streamRdr))
{ {
JObject jsonObject = (JObject) JToken.ReadFrom(jsonReader); JObject jsonObject = (JObject) JToken.ReadFrom(jsonReader);
return jsonObject.GetValue(key).ToObject<T>(); JToken value = jsonObject.GetValue(key);
if (null != value)
{
return value.ToObject<T>();
}
} }
} }
catch (ArgumentException) { } catch (ArgumentException) { }
@@ -389,8 +398,80 @@ namespace System.Management.Automation
/// <summary> /// <summary>
/// TODO: Should this return success fail or throw? /// TODO: Should this return success fail or throw?
/// /// </summary>
/// TODO: Catch exceptions /// <typeparam name="T"></typeparam>
/// <param name="fileName"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="addValue">Whether the key-value pair should be added or removed from the file</param>
private void UpdateValueInFile<T>(string fileName, string key, T value, bool addValue)
{
try
{
JObject objectToWrite = new JObject();
if (File.Exists(fileName))
{
// Since multiple properties can be in a single file, replacement
// is required instead of overwrite if a file already exists.
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
using (StreamReader streamRdr = new StreamReader(fs))
using (JsonTextReader jsonReader = new JsonTextReader(streamRdr))
{
JObject jsonObject = (JObject) JToken.ReadFrom(jsonReader);
IEnumerable<JProperty> properties = jsonObject.Properties();
foreach (JProperty property in properties)
{
if (String.Equals(property.Name, key, StringComparison.OrdinalIgnoreCase))
{
if (addValue)
{
// Add the updated value to the output object instead
// of the preexisting one
objectToWrite.Add(new JProperty(key, value));
}
// else the value is skipped so it is removed from the file
}
else
{
// This preserves existing properties that do not
// match the one to update
objectToWrite.Add(new JProperty(property));
}
}
}
}
else
{
if (addValue)
{
// The file doesn't exist, so create it with the new property
objectToWrite.Add(new JProperty(key, value));
}
}
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
using (StreamWriter streamWriter = new StreamWriter(fs))
using (JsonTextWriter jsonWriter = new JsonTextWriter(streamWriter))
{
objectToWrite.WriteTo(jsonWriter);
}
}
catch (ArgumentException) { }
//catch (ArgumentNullException) { }
catch (NotSupportedException) { }
catch (FileNotFoundException) { }
catch (System.IO.IOException) { }
//catch (DirectoryNotFoundException) { }
catch (System.Security.SecurityException) { }
catch (UnauthorizedAccessException) { }
//catch (PathTooLongException) { }
//catch (ArgumentOutOfRangeException) { }
}
/// <summary>
/// TODO: Should this return success, fail, or throw?
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="fileName"></param> /// <param name="fileName"></param>
@@ -398,233 +479,20 @@ namespace System.Management.Automation
/// <param name="value"></param> /// <param name="value"></param>
private void WriteValueToFile<T>(string fileName, string key, T value) private void WriteValueToFile<T>(string fileName, string key, T value)
{ {
JObject objectToWrite = new JObject(); UpdateValueInFile<T>(fileName, key, value, true);
}
if (File.Exists(fileName)) /// <summary>
{ /// TODO: Should this return success, fail, or throw?
// Since multiple properties can be in a single file, replacement /// </summary>
// is required instead of overwrite if a file already exists. /// <typeparam name="T"></typeparam>
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) /// <param name="fileName"></param>
using (StreamReader streamRdr = new StreamReader(fs)) /// <param name="key"></param>
using (JsonTextReader jsonReader = new JsonTextReader(streamRdr)) private void RemoveValueFromFile<T>(string fileName, string key)
{ {
JObject jsonObject = (JObject) JToken.ReadFrom(jsonReader); UpdateValueInFile<T>(fileName, key, default(T), false);
IEnumerable<JProperty> properties = jsonObject.Properties();
foreach (JProperty property in properties)
{
if (String.Equals(property.Name, key, StringComparison.OrdinalIgnoreCase))
{
// Add the updated value to the output object instead
// of the preexisting one
objectToWrite.Add(new JProperty(key, value));
}
else
{
// This preserves existing properties that do not
// match the one to update
objectToWrite.Add(new JProperty(property));
}
}
}
}
else
{
// The file doesn't exist, so create it with the new property
objectToWrite.Add(new JProperty(key, value));
}
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
using (StreamWriter streamWriter = new StreamWriter(fs))
using (JsonTextWriter jsonWriter = new JsonTextWriter(streamWriter))
{
objectToWrite.WriteTo(jsonWriter);
}
} }
} }
/*
/// <summary>
/// TODO: Add caching so for faster access? Would prevent runtime changes from affecting runtime reads though...
/// </summary>
internal class XmlConfigFileAccessor : PropertyAccessor
{
private string fileName = string.Empty;
internal XmlConfigFileAccessor()
{
Assembly assembly = typeof(PSObject).GetTypeInfo().Assembly;
var result = Path.GetDirectoryName(ClrFacade.GetAssemblyLocation(assembly));
fileName = result + "\\PowerShellConfig.xml";
}
internal override string GetApplicationBase(string version)
{
return ReadElementContentFromFileAsString("PSHome");
}
internal override string GetModulePath()
{
return ReadElementContentFromFileAsString("PSModulePath");
}
internal override string GetMachineShellPath(string shellId)
{
return ReadElementContentFromFileAsString("MachinePowerShellExePath");
}
internal override string GetMachineExecutionPolicy(string shellId)
{
return ReadElementContentFromFileAsString("MachineExecutionPolicy");
}
internal override void SetMachineExecutionPolicy(string shellId, string executionPolicy)
{
this.ReplaceElementValueInFile("MachineExecutionPolicy", executionPolicy);
}
internal override void RemoveMachineExecutionPolicy(string shellId)
{
this.RemoveElementFromFile("MachineExecutionPolicy");
}
private string ReadElementContentFromFileAsString(string elementName)
{
using (FileStream fs = new FileStream(this.fileName, FileMode.Open, FileAccess.Read))
using (StreamReader streamRdr = new StreamReader(fs))
{
XmlReaderSettings xmlReaderSettings =
InternalDeserializer.XmlReaderSettingsForUntrustedXmlDocument.Clone();
using (XmlReader reader = XmlReader.Create(streamRdr, xmlReaderSettings))
{
reader.MoveToContent();
if (reader.ReadToDescendant(elementName))
{
return reader.ReadElementContentAsString();
}
}
}
return string.Empty;
}
private void AddElementToFile(string elementName, string elementValue)
{
XmlDocument doc = new XmlDocument();
using (FileStream fs = new FileStream(this.fileName, FileMode.Open, FileAccess.Read))
using (StreamReader streamRdr = new StreamReader(fs))
{
doc.Load(streamRdr);
XmlNodeList nodeList = doc.GetElementsByTagName(elementName);
if (nodeList.Count > 0)
{
// Element exists. Replace it.
this.ReplaceElementValueInFile(elementName, elementValue);
}
else
{
XmlElement newElem = doc.CreateElement(elementName);
newElem.InnerText = elementValue;
doc.DocumentElement.AppendChild(newElem);
}
}
WriteDocumentToFile(doc);
}
private void RemoveElementFromFile(string elementName)
{
XmlDocument doc = new XmlDocument();
using (FileStream fs = new FileStream(this.fileName, FileMode.Open, FileAccess.Read))
using (StreamReader streamRdr = new StreamReader(fs))
{
doc.Load(streamRdr);
XmlNodeList nodeList = doc.GetElementsByTagName(elementName);
if (0 == nodeList.Count)
{
// There is nothing to do because the specified node does not exist
return;
}
else if (nodeList.Count > 1)
{
// throw docuemnt format exception? Nodes should be unique...
return;
}
else
{
XmlNode first = nodeList.Item(0);
if (null != first)
{
doc.DocumentElement.RemoveChild(first);
}
}
}
WriteDocumentToFile(doc);
}
private void ReplaceElementValueInFile(string elementName, string elementValue)
{
XmlDocument doc = new XmlDocument();
using (FileStream fs = new FileStream(this.fileName, FileMode.Open, FileAccess.Read))
using (StreamReader streamRdr = new StreamReader(fs))
{
doc.Load(streamRdr);
XmlNodeList nodeList = doc.GetElementsByTagName(elementName);
if (0 == nodeList.Count)
{
// There is nothing to do because the specified node does not exist
return;
}
else if (nodeList.Count > 1)
{
// throw docuemnt format exception? Nodes should be unique...
return;
}
else
{
XmlNode first = nodeList.Item(0);
if (null != first)
{
first.InnerText = elementValue;
}
}
// Alternate technique if the first one doesnt work
// Node is present, so replace it
//XmlElement newElem = doc.CreateElement("title");
//newElem.InnerText = elementValue;
//Replace the title element.
//doc.DocumentElement.ReplaceChild(elem, root.FirstChild);
}
WriteDocumentToFile(doc);
}
private void WriteDocumentToFile(XmlDocument doc)
{
using (FileStream fs = new FileStream(this.fileName, FileMode.Create, FileAccess.Write))
using (StreamWriter streamWriter = new StreamWriter(fs))
{
XmlWriterSettings xmlSettings = new XmlWriterSettings();
xmlSettings.Encoding = Encoding.UTF8;
xmlSettings.CloseOutput = true;
xmlSettings.Indent = true;
using (XmlWriter writer = XmlWriter.Create(streamWriter, xmlSettings))
{
doc.WriteTo(writer);
}
}
}
}
*/
internal class RegistryAccessor : PropertyAccessor internal class RegistryAccessor : PropertyAccessor
{ {
@@ -647,7 +515,7 @@ namespace System.Management.Automation
internal override string GetModulePath(ModulePathTarget target) internal override string GetModulePath(ModulePathTarget target)
{ {
// TODO: Better way to handle this? // TODO: Better way to handle this?
throw new InvalidEnumArgumentException(); throw new ArgumentException();
} }
/// <summary> /// <summary>
@@ -278,6 +278,18 @@ namespace System.Management.Automation
private static string[] s_productFolderDirectories; private static string[] s_productFolderDirectories;
/// <summary>
/// Specifies the per-user configuration settings directory in a platform agnostic manner.
/// Ex:
/// %LOCALAPPDATA%\PowerShell\<version>
/// </summary>
/// <returns>The current user's configuration settings directory</returns>
internal static string GetUserSettingsDirectory()
{
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return Path.Combine(appDataPath, "PowerShell", PSVersionInfo.PSVersion.ToString()); // TODO: This should be the external version#, but I don't see it anywhere in the code
}
private static string[] GetProductFolderDirectories() private static string[] GetProductFolderDirectories()
{ {
if (s_productFolderDirectories == null) if (s_productFolderDirectories == null)