blob: 663f29a0aa569e3816ab67c3a2a3321045998723 [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.Reflection;
using EnvDTE80;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NativeClientVSAddIn;
/// <summary>
/// This is a test class for PluginDebuggerGDBTest and is intended
/// to contain all PluginDebuggerGDB Unit Tests
/// </summary>
[TestClass]
public class PluginDebuggerGDBTest
{
/// <summary>
/// This holds the path to the NaCl solution used in these tests.
/// The NaCl solution is a valid nacl/pepper plug-in VS solution.
/// It is copied into the testing deployment directory and opened in some tests.
/// Because unit-tests run in any order, the solution should not be written to
/// in any tests.
/// </summary>
private static string naclSolution;
/// <summary>
/// The main visual studio object.
/// </summary>
private DTE2 dte_;
/// <summary>
/// Holds the default properties from property pages to use during tests.
/// </summary>
private MockPropertyManager properties_;
/// <summary>
/// Gets or sets the test context which provides information about,
/// and functionality for the current test run.
/// </summary>
public TestContext TestContext { get; set; }
/// <summary>
/// This is run one time before any test methods are called. Here we set-up a test-copy of a
/// new NaCl solution for use in the tests.
/// </summary>
/// <param name="testContext">Holds information about the current test run</param>
[ClassInitialize]
public static void ClassSetup(TestContext testContext)
{
DTE2 dte = TestUtilities.StartVisualStudioInstance();
try
{
naclSolution = TestUtilities.CreateBlankValidNaClSolution(
dte,
"PluginDebuggerGDBTest",
NativeClientVSAddIn.Strings.PepperPlatformName,
NativeClientVSAddIn.Strings.NaCl64PlatformName,
testContext);
}
finally
{
TestUtilities.CleanUpVisualStudioInstance(dte);
}
}
/// <summary>
/// This is run before each test to create test resources.
/// </summary>
[TestInitialize]
public void TestSetup()
{
dte_ = TestUtilities.StartVisualStudioInstance();
try
{
TestUtilities.AssertAddinLoaded(dte_, NativeClientVSAddIn.Strings.AddInName);
}
catch
{
TestUtilities.CleanUpVisualStudioInstance(dte_);
throw;
}
// Set up mock property manager to return the desired property values.
properties_ = new MockPropertyManager(
PropertyManager.ProjectPlatformType.NaCl,
delegate(string page, string name)
{
switch (page)
{
case "ConfigurationGeneral":
switch (name)
{
case "VSNaClSDKRoot": return System.Environment.GetEnvironmentVariable(
NativeClientVSAddIn.Strings.SDKPathEnvironmentVariable);
case "NaClManifestPath": return string.Empty;
case "ToolchainName": return "newlib";
case "TargetArchitecture": return "x86_64";
}
break;
case "WindowsLocalDebugger":
switch (name)
{
case "LocalDebuggerCommand":
return Environment.GetEnvironmentVariable("CHROME_PATH");
}
break;
case "Property":
switch (name)
{
case "ProjectDirectory": return TestContext.DeploymentDirectory;
case "PluginAssembly": return @"fake\Assembly\String";
}
break;
}
return null;
},
null);
}
/// <summary>
/// This is run after each test to clean up things created in TestSetup().
/// </summary>
[TestCleanup]
public void TestCleanup()
{
TestUtilities.CleanUpVisualStudioInstance(dte_);
}
/// <summary>
/// A test for the constructor.
/// </summary>
[TestMethod]
public void PluginDebuggerGDBConstructorTest()
{
// Check that a null dte fails.
try
{
PluginDebuggerBase nullDte = new PluginDebuggerGDB(null, properties_);
Assert.Fail("Using null DTE instance did not throw exception");
}
catch (ArgumentNullException)
{
// This is expected for a correct implementation.
}
catch
{
Assert.Fail("Using null DTE instance threw something other than ArgumentNullException");
}
// Check that a null PropertyManager fails.
try
{
PluginDebuggerBase nullDte = new PluginDebuggerGDB(dte_, null);
Assert.Fail("Using null property manager did not throw exception");
}
catch (ArgumentNullException)
{
// This is expected for a correct implementation.
}
catch
{
Assert.Fail("Using null property manager threw something other than ArgumentNullException");
}
}
/// <summary>
/// Tests that a plugin can be found.
/// </summary>
[TestMethod]
[DeploymentItem("NativeClientVSAddIn.dll")]
public void FindAndAttachToNaClPluginTest()
{
MockProcessSearcher processResults = new MockProcessSearcher();
using (PluginDebuggerGDB target = new PluginDebuggerGDB(dte_, properties_))
{
PluginDebuggerBase_Accessor targetBase = new PluginDebuggerBase_Accessor(
new PrivateObject(target, new PrivateType(typeof(PluginDebuggerBase))));
targetBase.debuggedChromeMainProcess_ = System.Diagnostics.Process.GetCurrentProcess();
uint currentProcId = (uint)targetBase.debuggedChromeMainProcess_.Id;
// Target nacl process flag.
string naclCommandLine = Strings.NaClLoaderFlag;
// Fake the list of processes on the system.
processResults.ProcessList.Add(
new ProcessInfo(
currentProcId,
currentProcId,
string.Empty,
Strings.NaClDebugFlag,
Strings.ChromeProcessName));
processResults.ProcessList.Add(
new ProcessInfo(1, currentProcId, string.Empty, string.Empty, "MyParentProcess"));
processResults.ProcessList.Add(
new ProcessInfo(11, 1, string.Empty, naclCommandLine, Strings.NaClProcessName));
// This is missing some relevant command line args, should not be attached to.
processResults.ProcessList.Add(
new ProcessInfo(13, 1, string.Empty, string.Empty, Strings.NaClProcessName));
// This doesn't have chrome process as their parent, so they should not be attached to.
processResults.ProcessList.Add(
new ProcessInfo(15, 15, string.Empty, naclCommandLine, Strings.NaClProcessName));
// Set the private value to the mock object (can't use accessor since no valid cast).
FieldInfo processSearcherField = typeof(PluginDebuggerBase).GetField(
"processSearcher_",
BindingFlags.NonPublic | BindingFlags.Instance);
processSearcherField.SetValue(targetBase.Target, processResults);
// Test that the correct processes are attached to.
bool goodNaCl = false;
var handler = new EventHandler<NativeClientVSAddIn.PluginDebuggerBase.PluginFoundEventArgs>(
delegate(object unused, NativeClientVSAddIn.PluginDebuggerBase.PluginFoundEventArgs args)
{
switch (args.ProcessID)
{
case 11:
if (goodNaCl)
{
Assert.Fail("Should not attach twice to same nacl process");
}
goodNaCl = true;
break;
case 13:
Assert.Fail("Should not attach to nacl process with bad args");
break;
case 15:
Assert.Fail("Should not attach to nacl process that is not "
+ "descendant of Visual Studio");
break;
default:
Assert.Fail("Should not attach to non-pepper/non-nacl process");
break;
}
});
target.PluginFoundEvent += handler;
target.FindAndAttachToPlugin(null, null);
target.PluginFoundEvent -= handler;
Assert.IsTrue(goodNaCl, "Failed to attach to NaCl process");
}
}
/// <summary>
/// A test for Attach. Implicitly tests CleanUpGDBProcess().
/// </summary>
[TestMethod]
[DeploymentItem("NativeClientVSAddIn.dll")]
public void AttachNaClGDBTest()
{
PluginDebuggerGDB_Accessor target = new PluginDebuggerGDB_Accessor(dte_, properties_);
string existingGDB = "AttachNaClGDBTest_existingGDB";
try
{
target.gdbProcess_ = TestUtilities.StartProcessForKilling(existingGDB, 20);
string existingInitFileName = Path.GetTempFileName();
target.gdbInitFileName_ = existingInitFileName;
// Visual studio won't allow adding a breakpoint unless it is associated with
// an existing file and valid line number, so use BlankValidSolution.
dte_.Solution.Open(naclSolution);
string fileName = "main.cpp";
string functionName = "NaClProjectInstance::HandleMessage";
int lineNumber = 39;
dte_.Debugger.Breakpoints.Add(Function: functionName);
dte_.Debugger.Breakpoints.Add(Line: lineNumber, File: fileName);
target.Attach(null, new PluginDebuggerBase.PluginFoundEventArgs(0));
Assert.IsTrue(File.Exists(target.gdbInitFileName_), "Init file not written");
var gdbCommands = new List<string>(File.ReadAllLines(target.gdbInitFileName_));
// Validate that the commands contain what we specified.
// The syntax itself is not validated since this add-in is not responsible for
// the syntax and it could change.
Assert.IsTrue(
gdbCommands.Exists(s => s.Contains(fileName) && s.Contains(lineNumber.ToString())),
"Line breakpoint not properly set");
Assert.IsTrue(
gdbCommands.Exists(s => s.Contains(functionName)),
"Function breakpoint not properly set");
// Note fake assembly string should be double escaped when passed to gdb.
Assert.IsTrue(
gdbCommands.Exists(s => s.Contains(functionName)),
@"fake\\Assembly\\String");
// Check that the pre-existing gdb process was killed and its init file cleaned up.
Assert.IsFalse(
TestUtilities.DoesProcessExist("python.exe", existingGDB),
"Failed to kill existing GDB process");
Assert.IsFalse(
File.Exists(existingInitFileName),
"Failed to delete existing temp gdb init file");
}
finally
{
if (dte_.Debugger.Breakpoints != null)
{
// Remove all breakpoints.
foreach (EnvDTE.Breakpoint bp in dte_.Debugger.Breakpoints)
{
bp.Delete();
}
}
// Clean up file if not erased.
if (!string.IsNullOrEmpty(target.gdbInitFileName_) && File.Exists(target.gdbInitFileName_))
{
File.Delete(target.gdbInitFileName_);
}
// Kill the gdb process if not killed.
if (target.gdbProcess_ != null && !target.gdbProcess_.HasExited)
{
target.gdbProcess_.Kill();
target.gdbProcess_.Dispose();
}
}
}
/// <summary>
/// A test for Dispose. Implicitly tests CleanUpGDBProcess().
/// </summary>
[TestMethod]
[DeploymentItem("NativeClientVSAddIn.dll")]
public void DisposeTest()
{
PluginDebuggerGDB_Accessor target = new PluginDebuggerGDB_Accessor(dte_, properties_);
string existingGDB = "DisposeTest_GDB";
try
{
target.gdbProcess_ = TestUtilities.StartProcessForKilling(existingGDB, 20);
string existingInitFileName = Path.GetTempFileName();
target.gdbInitFileName_ = existingInitFileName;
target.Dispose();
// Check that the pre-existing gdb process was killed and its init file cleaned up.
Assert.IsFalse(
TestUtilities.DoesProcessExist("python.exe", existingGDB),
"Failed to kill existing GDB process");
Assert.IsFalse(
File.Exists(existingInitFileName),
"Failed to delete existing temp gdb init file");
}
finally
{
// Clean up file if not erased.
if (!string.IsNullOrEmpty(target.gdbInitFileName_) && File.Exists(target.gdbInitFileName_))
{
File.Delete(target.gdbInitFileName_);
}
// Kill the gdb process if not killed.
if (target.gdbProcess_ != null && !target.gdbProcess_.HasExited)
{
target.gdbProcess_.Kill();
target.gdbProcess_.Dispose();
}
}
}
/// <summary>
/// A test for IsPluginProcess.
/// </summary>
[TestMethod]
[DeploymentItem("NativeClientVSAddIn.dll")]
public void IsNaClPluginProcessTest()
{
PluginDebuggerGDB_Accessor target = new PluginDebuggerGDB_Accessor(dte_, properties_);
ProcessInfo badProc = new ProcessInfo(
1, 1, string.Empty, Strings.ChromeRendererFlag, Strings.NaClProcessName);
ProcessInfo goodProc = new ProcessInfo(
1, 1, string.Empty, Strings.NaClLoaderFlag, Strings.NaClProcessName);
string goodMainChromeFlags = Strings.NaClDebugFlag;
string badMainChromeFlags = string.Format(
Strings.PepperProcessPluginFlagFormat, target.targetNexe_);
Assert.IsTrue(target.IsPluginProcess(goodProc, goodMainChromeFlags));
Assert.IsFalse(target.IsPluginProcess(goodProc, badMainChromeFlags));
Assert.IsFalse(target.IsPluginProcess(badProc, goodMainChromeFlags));
}
}
}