| Index: visual_studio/NativeClientVSAddIn/UnitTests/PluginDebuggerVSTest.cs | 
| diff --git a/visual_studio/NativeClientVSAddIn/UnitTests/PluginDebuggerVSTest.cs b/visual_studio/NativeClientVSAddIn/UnitTests/PluginDebuggerVSTest.cs | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..dd37310367ff681666bdbc2ce088b62ba3673819 | 
| --- /dev/null | 
| +++ b/visual_studio/NativeClientVSAddIn/UnitTests/PluginDebuggerVSTest.cs | 
| @@ -0,0 +1,286 @@ | 
| +// 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.Reflection; | 
| + | 
| +  using EnvDTE80; | 
| +  using Microsoft.VisualStudio.TestTools.UnitTesting; | 
| + | 
| +  using NativeClientVSAddIn; | 
| + | 
| +  /// <summary> | 
| +  /// This is a test class for PluginDebuggerVSTest and is intended | 
| +  /// to contain all PluginDebuggerVS Unit Tests | 
| +  /// </summary> | 
| +  [TestClass] | 
| +  public class PluginDebuggerVSTest | 
| +  { | 
| +    /// <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 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.Pepper, | 
| +        delegate(string page, string name) | 
| +        { | 
| +          switch (page) | 
| +          { | 
| +            case "ConfigurationGeneral": | 
| +              switch (name) | 
| +              { | 
| +                case "VSNaClSDKRoot": return System.Environment.GetEnvironmentVariable( | 
| +                    NativeClientVSAddIn.Strings.SDKPathEnvironmentVariable); | 
| +              } | 
| + | 
| +              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 PluginDebuggerVSConstructorTest() | 
| +    { | 
| +      // Check that a null dte fails. | 
| +      try | 
| +      { | 
| +        PluginDebuggerBase nullDte = new PluginDebuggerVS(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 PluginDebuggerVS(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> | 
| +    /// A test for FindAndAttachToPlugin. | 
| +    /// </summary> | 
| +    [TestMethod] | 
| +    [DeploymentItem("NativeClientVSAddIn.dll")] | 
| +    public void FindAndAttachToPepperPluginTest() | 
| +    { | 
| +      MockProcessSearcher processResults = new MockProcessSearcher(); | 
| + | 
| +      using (PluginDebuggerVS target = new PluginDebuggerVS(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; | 
| + | 
| +        string pluginLoadFlag = string.Format( | 
| +            Strings.PepperProcessPluginFlagFormat, properties_.PluginAssembly); | 
| +        string pepperCommandLine = string.Concat( | 
| +            pluginLoadFlag, " ", Strings.ChromeRendererFlag); | 
| + | 
| +        // 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(10, 1, string.Empty, pepperCommandLine, Strings.ChromeProcessName)); | 
| + | 
| +        // This is missing some relevant command line args, and should not be attached to. | 
| +        processResults.ProcessList.Add( | 
| +            new ProcessInfo(12, 1, string.Empty, pluginLoadFlag, Strings.ChromeProcessName)); | 
| + | 
| +        // This doesn't have this process as its parent, and should not be attached to. | 
| +        processResults.ProcessList.Add( | 
| +            new ProcessInfo(14, 14, string.Empty, pepperCommandLine, Strings.ChromeProcessName)); | 
| + | 
| +        // 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 goodPepper = false; | 
| +        var handler = new EventHandler<NativeClientVSAddIn.PluginDebuggerBase.PluginFoundEventArgs>( | 
| +          delegate(object unused, NativeClientVSAddIn.PluginDebuggerBase.PluginFoundEventArgs args) | 
| +          { | 
| +            switch (args.ProcessID) | 
| +            { | 
| +              case 10: | 
| +                if (goodPepper) | 
| +                { | 
| +                  Assert.Fail("Should not attach twice to same pepper process"); | 
| +                } | 
| + | 
| +                goodPepper = true; | 
| +                break; | 
| +              case 12: | 
| +                Assert.Fail("Should not attach to pepper process with bad args"); | 
| +                break; | 
| +              case 14: | 
| +                Assert.Fail("Should not attach to pepper 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(goodPepper, "Failed to attach to pepper process"); | 
| +      } | 
| +    } | 
| + | 
| +    /// <summary> | 
| +    /// Checks that VS properly attaches debugger. | 
| +    /// </summary> | 
| +    [TestMethod] | 
| +    [DeploymentItem("NativeClientVSAddIn.dll")] | 
| +    public void AttachVSDebuggerTest() | 
| +    { | 
| +      using (System.Diagnostics.Process dummyProc = TestUtilities.StartProcessForKilling( | 
| +          "DummyProc", 20)) | 
| +      { | 
| +        try | 
| +        { | 
| +          PluginDebuggerVS_Accessor target = new PluginDebuggerVS_Accessor(dte_, properties_); | 
| + | 
| +          var pluginFoundArgs = new NativeClientVSAddIn.PluginDebuggerBase.PluginFoundEventArgs( | 
| +              (uint)dummyProc.Id); | 
| +          target.Attach(null, pluginFoundArgs); | 
| + | 
| +          bool isBeingDebugged = false; | 
| +          foreach (EnvDTE.Process proc in dte_.Debugger.DebuggedProcesses) | 
| +          { | 
| +            if (proc.ProcessID == dummyProc.Id) | 
| +            { | 
| +              isBeingDebugged = true; | 
| +            } | 
| +          } | 
| + | 
| +          Assert.IsTrue(isBeingDebugged, "Visual Studio debugger did not attach"); | 
| +        } | 
| +        finally | 
| +        { | 
| +          if (dummyProc != null && !dummyProc.HasExited) | 
| +          { | 
| +            dummyProc.Kill(); | 
| +            dummyProc.Dispose(); | 
| +          } | 
| +        } | 
| +      } | 
| +    } | 
| + | 
| +    /// <summary> | 
| +    /// A test for IsPluginProcess. | 
| +    /// </summary> | 
| +    [TestMethod] | 
| +    [DeploymentItem("NativeClientVSAddIn.dll")] | 
| +    public void IsPepperPluginProcessTest() | 
| +    { | 
| +      PluginDebuggerVS_Accessor target = new PluginDebuggerVS_Accessor(dte_, properties_); | 
| + | 
| +      string identifierFlagTarget = | 
| +          string.Format(Strings.PepperProcessPluginFlagFormat, target.pluginAssembly_); | 
| +      string goodFlags = string.Concat(Strings.ChromeRendererFlag, ' ', identifierFlagTarget); | 
| + | 
| +      ProcessInfo badProc1 = new ProcessInfo( | 
| +          1, 1, string.Empty, goodFlags, Strings.NaClProcessName); | 
| +      ProcessInfo badProc2 = new ProcessInfo( | 
| +          1, 1, string.Empty, Strings.NaClLoaderFlag, Strings.ChromeProcessName); | 
| +      ProcessInfo badProc3 = new ProcessInfo( | 
| +          1, 1, string.Empty, Strings.ChromeRendererFlag, Strings.ChromeProcessName); | 
| +      ProcessInfo badProc4 = new ProcessInfo( | 
| +          1, 1, string.Empty, identifierFlagTarget, Strings.ChromeProcessName); | 
| +      ProcessInfo goodProc = new ProcessInfo( | 
| +          1, 1, string.Empty, goodFlags, Strings.ChromeProcessName); | 
| + | 
| +      Assert.IsTrue(target.IsPluginProcess(goodProc, string.Empty)); | 
| +      Assert.IsFalse(target.IsPluginProcess(badProc1, string.Empty)); | 
| +      Assert.IsFalse(target.IsPluginProcess(badProc2, string.Empty)); | 
| +      Assert.IsFalse(target.IsPluginProcess(badProc3, string.Empty)); | 
| +      Assert.IsFalse(target.IsPluginProcess(badProc4, string.Empty)); | 
| +    } | 
| +  } | 
| +} | 
|  |