microsoft/TypeAgent

Public

mirrored fromhttps://github.com/microsoft/TypeAgentAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
d4944c6517c9a96a3c419f827769f518913f1b75

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

dotnet/autoShell.Tests/AppActionHandlerTests.cs

188lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.ComponentModel;
5using System.Diagnostics;
6using System.Text.Json;
7using autoShell.Handlers;
8using autoShell.Logging;
9using autoShell.Services;
10using Moq;
11
12namespace autoShell.Tests;
13
14public class AppActionHandlerTests
15{
16 private readonly Mock<IAppRegistry> _appRegistryMock = new();
17 private readonly Mock<IProcessService> _processMock = new();
18 private readonly Mock<IWindowService> _windowMock = new();
19 private readonly Mock<ILogger> _loggerMock = new();
20 private readonly AppActionHandler _handler;
21
22 public AppActionHandlerTests()
23 {
24 _handler = new AppActionHandler(_appRegistryMock.Object, _processMock.Object, _windowMock.Object, _loggerMock.Object);
25 }
26
27 /// <summary>
28 /// Verifies that launching a non-running app starts it using its executable path.
29 /// </summary>
30 [Fact]
31 public void LaunchProgram_AppNotRunning_StartsViaPath()
32 {
33 _appRegistryMock.Setup(a => a.ResolveProcessName("chrome")).Returns("chrome");
34 _processMock.Setup(p => p.GetProcessesByName("chrome")).Returns([]);
35 _appRegistryMock.Setup(a => a.GetExecutablePath("chrome")).Returns("chrome.exe");
36
37 _handler.Handle("LaunchProgram", JsonDocument.Parse("""{"name":"chrome"}""").RootElement);
38
39 _processMock.Verify(p => p.Start(It.Is<ProcessStartInfo>(
40 psi => psi.FileName == "chrome.exe" && psi.UseShellExecute == true)), Times.Once);
41 }
42
43 /// <summary>
44 /// Verifies that launching an app with a configured working directory env var sets the working directory.
45 /// </summary>
46 [Fact]
47 public void LaunchProgram_WithWorkingDir_SetsWorkingDirectory()
48 {
49 _appRegistryMock.Setup(a => a.ResolveProcessName("github copilot")).Returns("github copilot");
50 _processMock.Setup(p => p.GetProcessesByName("github copilot")).Returns([]);
51 _appRegistryMock.Setup(a => a.GetExecutablePath("github copilot")).Returns("copilot.exe");
52 _appRegistryMock.Setup(a => a.GetWorkingDirectoryEnvVar("github copilot")).Returns("GITHUB_COPILOT_ROOT_DIR");
53
54 _handler.Handle("LaunchProgram", JsonDocument.Parse("""{"name":"github copilot"}""").RootElement);
55
56 _processMock.Verify(p => p.Start(It.Is<ProcessStartInfo>(
57 psi => psi.WorkingDirectory != "")), Times.Once);
58 }
59
60 /// <summary>
61 /// Verifies that launching an app with configured arguments passes them to the process start info.
62 /// </summary>
63 [Fact]
64 public void LaunchProgram_WithArguments_SetsArguments()
65 {
66 _appRegistryMock.Setup(a => a.ResolveProcessName("github copilot")).Returns("github copilot");
67 _processMock.Setup(p => p.GetProcessesByName("github copilot")).Returns([]);
68 _appRegistryMock.Setup(a => a.GetExecutablePath("github copilot")).Returns("copilot.exe");
69 _appRegistryMock.Setup(a => a.GetArguments("github copilot")).Returns("--allow-all-tools");
70
71 _handler.Handle("LaunchProgram", JsonDocument.Parse("""{"name":"github copilot"}""").RootElement);
72
73 _processMock.Verify(p => p.Start(It.Is<ProcessStartInfo>(
74 psi => psi.Arguments == "--allow-all-tools")), Times.Once);
75 }
76
77 /// <summary>
78 /// Verifies that when no executable path is available, the app is launched via its AppUserModelId through explorer.exe.
79 /// </summary>
80 [Fact]
81 public void LaunchProgram_NoPath_UsesAppUserModelId()
82 {
83 _appRegistryMock.Setup(a => a.ResolveProcessName("calculator")).Returns("calculator");
84 _processMock.Setup(p => p.GetProcessesByName("calculator")).Returns([]);
85 _appRegistryMock.Setup(a => a.GetExecutablePath("calculator")).Returns((string)null!);
86 _appRegistryMock.Setup(a => a.GetAppUserModelId("calculator")).Returns("Microsoft.WindowsCalculator");
87
88 _handler.Handle("LaunchProgram", JsonDocument.Parse("""{"name":"calculator"}""").RootElement);
89
90 _processMock.Verify(p => p.Start(It.Is<ProcessStartInfo>(
91 psi => psi.FileName == "explorer.exe")), Times.Once);
92 }
93
94 /// <summary>
95 /// Verifies that closing a program resolves its process name and looks up running processes.
96 /// Note: the actual <see cref="System.Diagnostics.Process.CloseMainWindow"/> call path cannot be unit-tested because
97 /// <see cref="System.Diagnostics.Process.MainWindowHandle"/> is not virtual and cannot be mocked.
98 /// </summary>
99 [Fact]
100 public void CloseProgram_ResolvesProcessNameAndLooksUpProcesses()
101 {
102 _appRegistryMock.Setup(a => a.ResolveProcessName("notepad")).Returns("notepad");
103 // Return a real (albeit useless in test) empty array to avoid null ref;
104 // We cannot easily mock Process objects, so we verify the lookup was attempted.
105 _processMock.Setup(p => p.GetProcessesByName("notepad")).Returns([]);
106
107 _handler.Handle("CloseProgram", JsonDocument.Parse("""{"name":"notepad"}""").RootElement);
108
109 _processMock.Verify(p => p.GetProcessesByName("notepad"), Times.Once);
110 }
111
112 /// <summary>
113 /// Verifies that closing a program that is not running does not throw an exception.
114 /// </summary>
115 [Fact]
116 public void CloseProgram_NotRunning_DoesNothing()
117 {
118 _appRegistryMock.Setup(a => a.ResolveProcessName("notepad")).Returns("notepad");
119 _processMock.Setup(p => p.GetProcessesByName("notepad")).Returns([]);
120
121 var ex = Record.Exception(() => _handler.Handle("CloseProgram", JsonDocument.Parse("""{"name":"notepad"}""").RootElement));
122
123 Assert.Null(ex);
124 }
125
126 /// <summary>
127 /// Verifies that the ListAppNames command invokes <see cref="IAppRegistry.GetAllAppNames"/> on the app registry.
128 /// </summary>
129 [Fact]
130 public void ListAppNames_CallsGetAllAppNames()
131 {
132 _appRegistryMock.Setup(a => a.GetAllAppNames()).Returns(["notepad", "chrome"]);
133
134 _handler.Handle("ListAppNames", JsonDocument.Parse("""{"name":""}""").RootElement);
135
136 _appRegistryMock.Verify(a => a.GetAllAppNames(), Times.Once);
137 }
138
139 /// <summary>
140 /// Verifies that launching an already-running app raises its window instead of starting a new process.
141 /// </summary>
142 [Fact]
143 public void LaunchProgram_AlreadyRunning_RaisesWindow()
144 {
145 _appRegistryMock.Setup(a => a.ResolveProcessName("notepad")).Returns("notepad");
146 _processMock.Setup(p => p.GetProcessesByName("notepad")).Returns([Process.GetCurrentProcess()]);
147 _appRegistryMock.Setup(a => a.GetExecutablePath("notepad")).Returns("notepad.exe");
148
149 _handler.Handle("LaunchProgram", JsonDocument.Parse("""{"name":"notepad"}""").RootElement);
150
151 _windowMock.Verify(w => w.RaiseWindow("notepad", "notepad.exe"), Times.Once);
152 _processMock.Verify(p => p.Start(It.IsAny<ProcessStartInfo>()), Times.Never);
153 }
154
155 /// <summary>
156 /// Verifies that a <see cref="System.ComponentModel.Win32Exception"/> on first launch attempt triggers a fallback retry using the friendly name.
157 /// </summary>
158 [Fact]
159 public void LaunchProgram_Win32Exception_FallsBackToFriendlyName()
160 {
161 _appRegistryMock.Setup(a => a.ResolveProcessName("myapp")).Returns("myapp");
162 _processMock.Setup(p => p.GetProcessesByName("myapp")).Returns([]);
163 _appRegistryMock.Setup(a => a.GetExecutablePath("myapp")).Returns("myapp.exe");
164 _processMock.SetupSequence(p => p.Start(It.IsAny<ProcessStartInfo>()))
165 .Throws(new Win32Exception("not found"))
166 .Returns(Process.GetCurrentProcess());
167
168 _handler.Handle("LaunchProgram", JsonDocument.Parse("""{"name":"myapp"}""").RootElement);
169
170 _processMock.Verify(p => p.Start(It.IsAny<ProcessStartInfo>()), Times.Exactly(2));
171 }
172
173 /// <summary>
174 /// Verifies that launching an app with no path and no AppUserModelId does not start any process.
175 /// </summary>
176 [Fact]
177 public void LaunchProgram_NoPathNoAppModelId_DoesNothing()
178 {
179 _appRegistryMock.Setup(a => a.ResolveProcessName("unknown")).Returns("unknown");
180 _processMock.Setup(p => p.GetProcessesByName("unknown")).Returns([]);
181 _appRegistryMock.Setup(a => a.GetExecutablePath("unknown")).Returns((string)null!);
182 _appRegistryMock.Setup(a => a.GetAppUserModelId("unknown")).Returns((string)null!);
183
184 _handler.Handle("LaunchProgram", JsonDocument.Parse("""{"name":"unknown"}""").RootElement);
185
186 _processMock.Verify(p => p.Start(It.IsAny<ProcessStartInfo>()), Times.Never);
187 }
188}
189