/***************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
This code is licensed under the Visual Studio SDK license terms.
THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
***************************************************************************/
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Vsip;
namespace Microsoft.VisualStudio.TestTools.Samples
{
/// <summary>
/// UI control for my host adapter configuartion. Hosted inside test run config editor.
/// It contains a data grid view where you could define environment variables.
/// </summary>
public partial class MyHostAdapterRunConfigControl : UserControl, IRunConfigurationCustomHostEditor
{
#region Private Data
private MyHostRunConfigData m_config = new MyHostRunConfigData();
private bool m_isLoading = false;
#endregion
#region Constructor
public MyHostAdapterRunConfigControl()
{
InitializeComponent();
m_envVarDataGridView.CellValueChanged += new DataGridViewCellEventHandler(DataGridView_CellValueChanged);
m_envVarDataGridView.RowsRemoved += new DataGridViewRowsRemovedEventHandler(DataGridView_RowsRemoved);
m_envVarDataGridView.RowValidating += new DataGridViewCellCancelEventHandler(DataGridView_RowValidating);
m_envVarDataGridView.RowValidated += new DataGridViewCellEventHandler(DataGridView_RowValidated);
}
#endregion
#region Event Handlers
void DataGridView_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
{
UpdateConfig();
}
void DataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
UpdateConfig();
}
/// <summary>
/// Validate the row. A row is invalid if there is an empty variable name, or duplicate variable names.
/// </summary>
void DataGridView_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
{
// Save existing names to find out duplicates
StringDictionary names = new StringDictionary();
e.Cancel = false;
foreach (DataGridViewRow row in m_envVarDataGridView.Rows)
{
if (IsEmptyCell(row.Cells[0]) && !IsEmptyCell(row.Cells[1]))
{
e.Cancel = true;
row.ErrorText = "Empty name.";
return;
}
else if (!IsEmptyCell(row.Cells[0]))
{
string name = row.Cells[0].Value.ToString();
if (names.ContainsKey(name))
{
e.Cancel = true;
row.ErrorText = "Duplicate name.";
return;
}
else
{
names.Add(name, string.Empty);
}
}
}
}
/// <summary>
/// Clear the error text after validation is done.
/// </summary>
void DataGridView_RowValidated(object sender, DataGridViewCellEventArgs e)
{
// Clear the error text
m_envVarDataGridView.Rows[e.RowIndex].ErrorText = null;
}
#endregion
#region Private Methods
/// <summary>
/// Help method. A cell is empty if either the value is null, or an emtpy string.
/// </summary>
private bool IsEmptyCell(DataGridViewCell cell)
{
return cell.Value == null || string.IsNullOrEmpty(cell.Value.ToString());
}
/// <summary>
/// Update the config object based on user's input.
/// </summary>
private void UpdateConfig()
{
if (!m_isLoading)
{
// Notify the editor that data get modified.
if (null != DataGetDirty)
{
DataGetDirty(this, EventArgs.Empty);
}
m_config.EnvironmentVariables.Clear();
foreach (DataGridViewRow row in m_envVarDataGridView.Rows)
{
// If the cell is not empty, and not duplicate, we add it to the dictionary
if (!IsEmptyCell(row.Cells[0]) && !m_config.EnvironmentVariables.ContainsKey(row.Cells[0].Value.ToString()))
{
m_config.EnvironmentVariables.Add(row.Cells[0].Value.ToString(),
(null == row.Cells[1].Value) ? string.Empty : row.Cells[1].Value.ToString());
}
}
}
}
#endregion
#region IRunConfigurationEditor
/// <summary>
/// Initialize the editor to a default state based on given test run.
/// </summary>
/// <param name="serviceProvider"></param>
/// <param name="run">Obselete. Always null.</param>
void IRunConfigurationEditor.Initialize(System.IServiceProvider serviceProvider, TestRun run)
{
// Nothing to do here.
}
/// <summary>
/// Fire this event when data are modified in this editor.
/// </summary>
public event EventHandler DataGetDirty;
/// <summary>
/// Handle the event that core (non-host and not-test-specific) run config data are modified outside this editor.
/// </summary>
/// <param name="sender"></param>
/// <param name="dirtyEventArgs">contains run config object that is changed outside</param>
void IRunConfigurationEditor.OnCommonDataDirty(object sender, CommonRunConfigurationDirtyEventArgs dirtyEventArgs)
{
// My test config does not depend on other data contained in the run config.
}
/// <summary>
/// Desciption about this editor is displayed in the help panel of main run config editor.
/// </summary>
string IRunConfigurationEditor.Description
{
get
{
return "This host adapter only supports unit test and my test. It sets environment variables that affect your unit test execution. It also affects how my test decides the test result. A \"my test\" passes when the process exit code is equal to value of environment variable PassedCode.";
}
}
/// <summary>
/// The keyword that is hooked up with the help topic.
/// </summary>
string IRunConfigurationEditor.HelpKeyword
{
get
{
return null;
}
}
/// Verify the data in the editor. Prompt the user when neccessary.
/// </summary>
/// <returns>true if the data are correct and don't need correction; otherwise, false.</returns>
bool IRunConfigurationEditor.VerifyData()
{
// DataGridView already does validation.
return true;
}
#endregion
#region IRunConfigurationCustomHostEditor
/// <summary>
/// The host adapter type that this editor is used for.
/// </summary>
string IRunConfigurationCustomHostEditor.HostType
{
get
{
return MyHostAdapter.Name;
}
}
/// <summary>
/// Called by the main editor to load the data into UI.
/// </summary>
/// <param name="data">host specific data</param>
void IRunConfigurationCustomHostEditor.SetData(IHostSpecificRunConfigurationData data)
{
// We need to turn off the event handlers when loading data into UI.
m_isLoading = true;
try
{
m_config = (MyHostRunConfigData)data;
m_envVarDataGridView.Rows.Clear();
foreach (string name in m_config.EnvironmentVariables.Keys)
{
DataGridViewRow row = new DataGridViewRow();
DataGridViewTextBoxCell cell1 = new DataGridViewTextBoxCell();
cell1.Value = name;
DataGridViewTextBoxCell cell2 = new DataGridViewTextBoxCell();
cell2.Value = m_config.EnvironmentVariables[name];
row.Cells.Add(cell1);
row.Cells.Add(cell2);
m_envVarDataGridView.Rows.Add(row);
}
}
finally
{
m_isLoading = false;
}
}
/// <summary>
/// Main editor is asking for the current host specific data.
/// </summary>
/// <returns></returns>
IHostSpecificRunConfigurationData IRunConfigurationCustomHostEditor.GetData()
{
return m_config;
}
#endregion
}
/// <summary>
/// Test run level configuration information of my host adapter.
/// The information is accessible to my host adapter.
/// Must be Serializable.
/// </summary>
[Serializable]
class MyHostRunConfigData : IHostSpecificRunConfigurationData
{
#region Private Data
private StringDictionary m_envVarDictionary = new StringDictionary();
#endregion
#region Properties
/// <summary>
/// Environment variables that will be set by the host adapter.
/// </summary>
public StringDictionary EnvironmentVariables
{
get
{
return m_envVarDictionary;
}
}
#endregion
#region ICloneable
object ICloneable.Clone()
{
MyHostRunConfigData copy = new MyHostRunConfigData();
copy.EnvironmentVariables.Clear();
foreach (string name in this.EnvironmentVariables.Keys)
{
copy.EnvironmentVariables.Add(name, this.EnvironmentVariables[name]);
}
return copy;
}
#endregion
}
}