| // 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)); |
| } |
| } |
| } |