blob: 20a78dd9da396881864155f00e467f26a632d3db [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
namespace UnitTests
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.VCProjectEngine;
/// <summary>
/// This class contains utilities for running tests.
/// </summary>
public static class TestUtilities
{
/// <summary>
/// Name of the NaCl project in BlankValidSolution.
/// </summary>
public const string BlankNaClProjectName = @"NaClProject";
/// <summary>
/// Uniquename of the NaCl project in BlankValidSolution.
/// </summary>
public const string BlankNaClProjectUniqueName = @"NaClProject\NaClProject.vcxproj";
/// <summary>
/// Uniquename of the non-NaCl project in BlankValidSolution.
/// </summary>
public const string NotNaClProjectUniqueName = @"NotNaCl\NotNaCl.csproj";
/// <summary>
/// A generic boolean statement to be used with RetryWithTimeout.
/// </summary>
/// <returns>True if the statement is true, false if false.</returns>
public delegate bool RetryStatement();
/// <summary>
/// This starts an instance of Visual Studio and get its DTE object.
/// </summary>
/// <returns>DTE of the started instance.</returns>
public static DTE2 StartVisualStudioInstance()
{
// Set up filter to handle threading events and automatically retry calls
// to dte which fail because dte is busy.
ComMessageFilter.Register();
Type visualStudioType = Type.GetTypeFromProgID("VisualStudio.DTE.10.0");
DTE2 visualStudio = Activator.CreateInstance(visualStudioType) as DTE2;
if (visualStudio == null)
{
throw new Exception("Visual Studio failed to start");
}
visualStudio.MainWindow.Visible = true;
return visualStudio;
}
/// <summary>
/// This properly cleans up after StartVisualStudioInstance().
/// </summary>
/// <param name="dte">Dte instance returned by StartVisualStudioInstance().</param>
public static void CleanUpVisualStudioInstance(DTE2 dte)
{
if (dte != null)
{
if (dte.Solution != null)
{
dte.Solution.Close();
}
dte.Quit();
}
// Stop the message filter.
ComMessageFilter.Revoke();
}
/// <summary>
/// Creates a blank valid NaCl project with up-to-date settings. The path to the new solution
/// is returned.
/// </summary>
/// <param name="dte">Interface to an open Visual Studio instance to use.</param>
/// <param name="name">Name to give newly created solution.</param>
/// <param name="pepperCopyFrom">Platform name to copy existing settings from to pepper.</param>
/// <param name="naclCopyFrom">Platform name to copy existing settings from to NaCl.</param>
/// <param name="testContext">Test context used for finding deployment directory.</param>
/// <returns>Path to the newly created solution.</returns>
public static string CreateBlankValidNaClSolution(
DTE2 dte, string name, string pepperCopyFrom, string naclCopyFrom, TestContext testContext)
{
const string BlankSolution = "BlankValidSolution";
string newSolutionDir = Path.Combine(testContext.DeploymentDirectory, name);
string newSolution = Path.Combine(newSolutionDir, BlankSolution + ".sln");
CopyDirectory(Path.Combine(testContext.DeploymentDirectory, BlankSolution), newSolutionDir);
try
{
dte.Solution.Open(newSolution);
Project proj = dte.Solution.Projects.Item(BlankNaClProjectUniqueName);
// Order matters if copying from the other Native Client type.
if (pepperCopyFrom.Equals(NativeClientVSAddIn.Strings.NaClPlatformName))
{
proj.ConfigurationManager.AddPlatform(
NativeClientVSAddIn.Strings.NaClPlatformName, naclCopyFrom, true);
proj.ConfigurationManager.AddPlatform(
NativeClientVSAddIn.Strings.PepperPlatformName, pepperCopyFrom, true);
}
else
{
proj.ConfigurationManager.AddPlatform(
NativeClientVSAddIn.Strings.PepperPlatformName, pepperCopyFrom, true);
proj.ConfigurationManager.AddPlatform(
NativeClientVSAddIn.Strings.NaClPlatformName, naclCopyFrom, true);
}
// Set the active solution configuration to Debug|NaCl.
SetSolutionConfiguration(
dte, BlankNaClProjectUniqueName, "Debug", NativeClientVSAddIn.Strings.NaClPlatformName);
proj.Save();
dte.Solution.SaveAs(newSolution);
}
finally
{
if (dte.Solution != null)
{
dte.Solution.Close();
}
}
return newSolution;
}
/// <summary>
/// This returns the text contained in the given output window pane.
/// </summary>
/// <param name="pane">Pane to get text from.</param>
/// <returns>Text in the window.</returns>
public static string GetPaneText(OutputWindowPane pane)
{
TextSelection selection = pane.TextDocument.Selection;
selection.StartOfDocument(false);
selection.EndOfDocument(true);
return selection.Text;
}
/// <summary>
/// This starts a python process that just sleeps waiting to be killed.
/// It can be used with DoesProcessExist() to verify that a process started/exited.
/// </summary>
/// <param name="identifierString">
/// A unique string to identify the process via its command line arguments.
/// </param>
/// <param name="timeout">Time in seconds to wait before process exits on its own.</param>
/// <returns>The process object that was started.</returns>
public static System.Diagnostics.Process StartProcessForKilling(
string identifierString, int timeout)
{
string args = string.Format(
"-c \"import time; time.sleep({0}); print '{1}'\"",
timeout,
identifierString);
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "python.exe";
proc.StartInfo.Arguments = args;
proc.Start();
return proc;
}
/// <summary>
/// This returns true if there is a running process that has command line arguments
/// containing the given Strings. The search is case-insensitive.
/// </summary>
/// <param name="processName">Name of the process executable.</param>
/// <param name="commandLineIdentifiers">Strings to check for.</param>
/// <returns>True if some process has the Strings in its command line arguments.</returns>
public static bool DoesProcessExist(string processName, params string[] commandLineIdentifiers)
{
List<string> results = new List<string>();
string query =
string.Format("select CommandLine from Win32_Process where Name='{0}'", processName);
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
{
using (ManagementObjectCollection result = searcher.Get())
{
foreach (ManagementObject process in result)
{
string commandLine = process["CommandLine"] as string;
if (string.IsNullOrEmpty(commandLine))
{
break;
}
// Check if the command line contains each of the required identifiers.
if (commandLineIdentifiers.All(i => commandLine.Contains(i)))
{
return true;
}
}
}
}
return false;
}
/// <summary>
/// Sets the active configuration for the solution by specifying the configuration name
/// and platform name. A solution configuration containing a project configuration that has
/// the config and platform names specified for the specified project is selected.
/// </summary>
/// <param name="dte">The main visual studio object.</param>
/// <param name="projectUniqueName">UniqueName of the project to match.</param>
/// <param name="configurationName">Ex: "Debug" or "Release".</param>
/// <param name="platformName">Ex: "Win32" or "NaCl" or "PPAPI".</param>
public static void SetSolutionConfiguration(
DTE2 dte,
string projectUniqueName,
string configurationName,
string platformName)
{
foreach (EnvDTE.SolutionConfiguration config in
dte.Solution.SolutionBuild.SolutionConfigurations)
{
EnvDTE.SolutionContext context = null;
try
{
context = config.SolutionContexts.Item(projectUniqueName);
}
catch (ArgumentException)
{
throw new Exception(
string.Format("Project unique name not found in solution: {0}", projectUniqueName));
}
if (context == null)
{
throw new Exception("Failed to get solution context");
}
if (context.PlatformName == platformName && context.ConfigurationName == configurationName)
{
config.Activate();
return;
}
}
throw new Exception(string.Format(
"Matching configuration not found for {0}: {1}|{2}",
projectUniqueName,
platformName,
configurationName));
}
/// <summary>
/// Returns a VCConfiguration object with a matching configuration name and platform type.
/// </summary>
/// <param name="project">Project to get the configuration from.</param>
/// <param name="name">Name of configuration (e.g. 'Debug').</param>
/// <param name="platform">Name of the platform (e.g. 'NaCl').</param>
/// <returns>A matching VCConfiguration object.</returns>
public static VCConfiguration GetVCConfiguration(Project project, string name, string platform)
{
VCProject vcproj = (VCProject)project.Object;
IVCCollection configs = vcproj.Configurations;
foreach (VCConfiguration config in configs)
{
if (config.ConfigurationName == name && config.Platform.Name == platform)
{
return config;
}
}
throw new Exception(
string.Format("Project does not have configuration: {0}|{1}", platform, name));
}
/// <summary>
/// Tests that a given property has a specific value in a certain VCConfiguration
/// </summary>
/// <param name="configuration">Gives the platform and configuration type</param>
/// <param name="pageName">Property page name where property resides.</param>
/// <param name="propertyName">Name of the property to check.</param>
/// <param name="expectedValue">Expected value of the property.</param>
/// <param name="ignoreCase">Ignore case when comparing the expected and actual values.</param>
public static void AssertPropertyEquals(
VCConfiguration configuration,
string pageName,
string propertyName,
string expectedValue,
bool ignoreCase)
{
IVCRulePropertyStorage rule = configuration.Rules.Item(pageName);
string callInfo = string.Format(
"Page: {0}, Property: {1}, Configuration: {2}",
pageName,
propertyName,
configuration.ConfigurationName);
Assert.AreEqual(
expectedValue,
rule.GetUnevaluatedPropertyValue(propertyName),
ignoreCase,
callInfo);
}
/// <summary>
/// Tests that a given property contains a specific string in a certain VCConfiguration
/// </summary>
/// <param name="configuration">Gives the platform and configuration type</param>
/// <param name="pageName">Property page name where property resides.</param>
/// <param name="propertyName">Name of the property to check.</param>
/// <param name="expectedValue">Expected string to contain.</param>
/// <param name="ignoreCase">Ignore case when comparing the expected and actual values.</param>
public static void AssertPropertyContains(
VCConfiguration configuration,
string pageName,
string propertyName,
string expectedValue,
bool ignoreCase)
{
StringComparison caseSensitive = ignoreCase ?
StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
IVCRulePropertyStorage rule = configuration.Rules.Item(pageName);
string propertyValue = rule.GetUnevaluatedPropertyValue(propertyName);
string message = string.Format(
"{0} should be contained in {1}. Page: {2}, Property: {3}, Configuration: {4}",
expectedValue,
propertyValue,
pageName,
propertyName,
configuration.ConfigurationName);
Assert.IsTrue(propertyValue.Contains(expectedValue, caseSensitive), message);
}
/// <summary>
/// Tests that a given property is not null or empty.
/// </summary>
/// <param name="configuration">Gives the platform and configuration type</param>
/// <param name="pageName">Property page name where property resides.</param>
/// <param name="propertyName">Name of the property to check.</param>
public static void AssertPropertyIsNotNullOrEmpty(
VCConfiguration configuration,
string pageName,
string propertyName)
{
IVCRulePropertyStorage rule = configuration.Rules.Item(pageName);
string propertyValue = rule.GetUnevaluatedPropertyValue(propertyName);
string message = string.Format(
"{0} was null or empty. Page: {1}, Configuration: {2}",
propertyName,
pageName,
configuration.ConfigurationName);
Assert.IsFalse(string.IsNullOrEmpty(propertyValue), message);
}
/// <summary>
/// Ensures that the add-in is configured to load on start. If it isn't then some tests may
/// unexpectedly fail, this check helps catch that problem early.
/// </summary>
/// <param name="dte">The main Visual Studio interface.</param>
/// <param name="addInName">The name of the add-in to check if loaded.</param>
public static void AssertAddinLoaded(DTE2 dte, string addInName)
{
bool found = false;
foreach (AddIn addin in dte.AddIns)
{
if (addin.Connected && addInName.Equals(addin.Name))
{
found = true;
break;
}
}
Assert.IsTrue(found, "Add-in is not configured to load on start.");
}
/// <summary>
/// Will retry the given statement up to maxRetry times while pausing between each try for
/// the given interval.
/// </summary>
/// <param name="test">Generic boolean statement.</param>
/// <param name="interval">Amount of time to wait between each retry.</param>
/// <param name="maxRetry">Maximum number of retries.</param>
/// <param name="message">Message to print on failure.</param>
public static void AssertTrueWithTimeout(
RetryStatement test, TimeSpan interval, int maxRetry, string message)
{
for (int tryCount = 0; tryCount <= maxRetry; tryCount++)
{
if (test.Invoke())
{
return;
}
System.Threading.Thread.Sleep(interval);
}
throw new Exception(string.Format("Statement timed out. {0}", message));
}
/// <summary>
/// Extends the string class to allow checking if a string contains another string
/// allowing a comparison type (such as case-insensitivity).
/// </summary>
/// <param name="source">Base string to search.</param>
/// <param name="toCheck">String to check if contained within base string.</param>
/// <param name="comparison">Comparison type.</param>
/// <returns>True if toCheck is contained in source.</returns>
public static bool Contains(this string source, string toCheck, StringComparison comparison)
{
return source.IndexOf(toCheck, comparison) != -1;
}
/// <summary>
/// Copies the entire contents of a directory and sub directories.
/// </summary>
/// <param name="source">Directory to copy from.</param>
/// <param name="dest">Directory to copy to.</param>
public static void CopyDirectory(string source, string dest)
{
DirectoryInfo dir = new DirectoryInfo(source);
if (!dir.Exists)
{
throw new DirectoryNotFoundException(source);
}
if (!Directory.Exists(dest))
{
Directory.CreateDirectory(dest);
}
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string path = Path.Combine(dest, file.Name);
file.CopyTo(path, false);
}
foreach (DirectoryInfo subdir in dir.GetDirectories())
{
string path = Path.Combine(dest, subdir.Name);
CopyDirectory(subdir.FullName, path);
}
}
}
}