-
Notifications
You must be signed in to change notification settings - Fork 31
feat: Launch selected Godot version straight from the CLI! #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| namespace Chickensoft.GodotEnv.Tests.features.godot.commands; | ||
|
|
||
| using System; | ||
| using System.IO; | ||
| using System.Threading.Tasks; | ||
| using Chickensoft.GodotEnv.Common.Clients; | ||
| using Chickensoft.GodotEnv.Features.Godot.Commands; | ||
| using Chickensoft.GodotEnv.Features.Godot.Domain; | ||
| using Chickensoft.GodotEnv.Features.Godot.Models; | ||
| using CliFx.Infrastructure; | ||
| using Common.Models; | ||
| using Common.Utilities; | ||
| using global::GodotEnv.Common.Utilities; | ||
| using Moq; | ||
| using Shouldly; | ||
| using Xunit; | ||
|
|
||
| public sealed class GodotLaunchCommandTest : IDisposable { | ||
| private readonly MockSystemInfo _systemInfo; | ||
| private readonly Mock<IExecutionContext> _context; | ||
| private readonly Mock<IGodotContext> _godotContext; | ||
| private readonly Mock<IGodotEnvironment> _environment; | ||
| private readonly Mock<IGodotRepository> _godotRepo; | ||
| private readonly Mock<IProcessRunner> _processRunner; | ||
| private readonly FakeInMemoryConsole _console; | ||
| private readonly Mock<IFileClient> _fileClient; | ||
| private readonly Log _log; | ||
|
|
||
| public GodotLaunchCommandTest() { | ||
| _systemInfo = new MockSystemInfo(OSType.Linux, CPUArch.X64); | ||
| _context = new Mock<IExecutionContext>(); | ||
| _godotContext = new Mock<IGodotContext>(); | ||
| _environment = new Mock<IGodotEnvironment>(); | ||
| _godotRepo = new Mock<IGodotRepository>(); | ||
| _processRunner = new Mock<IProcessRunner>(); | ||
| _console = new FakeInMemoryConsole(); | ||
| _fileClient = new Mock<IFileClient>(); | ||
|
|
||
|
|
||
| _environment.Setup(env => env.SystemInfo).Returns(_systemInfo); | ||
| _godotContext.SetupGet(c => c.GodotRepo).Returns(_godotRepo.Object); | ||
| _godotContext.Setup(c => c.Platform).Returns(_environment.Object); | ||
| _context.SetupGet(context => context.Godot).Returns(_godotContext.Object); | ||
| _log = new Log(_systemInfo, _console); | ||
| _context.Setup(context => context.CreateLog(_console)).Returns(_log); | ||
|
|
||
| _godotRepo.SetupGet(r => r.ProcessRunner).Returns(_processRunner.Object); | ||
| } | ||
|
|
||
| public void Dispose() { | ||
| _console.Dispose(); | ||
| Environment.SetEnvironmentVariable("GODOT", null); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task Launches_Godot_When_Env_Variable_Is_Valid() { | ||
| var godotPath = Path.Combine(Path.GetTempPath(), "godot-test-launcher"); | ||
|
|
||
| // Create a fake Godot binary | ||
| File.WriteAllText(godotPath, string.Empty); | ||
| Environment.SetEnvironmentVariable("GODOT", godotPath); | ||
|
|
||
| var launchCommand = new GodotLaunchCommand(_context.Object); | ||
|
|
||
| await launchCommand.ExecuteAsync(_console); | ||
|
|
||
| _processRunner.Verify(p => | ||
| p.RunDetached(godotPath, Array.Empty<string>()), Times.Once | ||
| ); | ||
|
|
||
| _log.ToString().ShouldContain($"Launching Godot from {godotPath}"); | ||
|
|
||
| // Clean up | ||
| File.Delete(godotPath); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task Fails_When_GODOT_Variable_Is_Unset() { | ||
| Environment.SetEnvironmentVariable("GODOT", null); | ||
|
|
||
| var launchCommand = new GodotLaunchCommand(_context.Object); | ||
| await launchCommand.ExecuteAsync(_console); | ||
|
|
||
| _processRunner.Verify(p => | ||
| p.RunDetached(It.IsAny<string>(), It.IsAny<string[]>()), Times.Never | ||
| ); | ||
|
|
||
| _log.ToString().ShouldContain("The GODOT environment variable is not set"); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task Fails_When_GODOT_Target_Does_Not_Exist() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Besides the underscores, I'd suggest using |
||
| var godotPath = "/nonexistent/godot"; | ||
| Environment.SetEnvironmentVariable("GODOT", godotPath); | ||
|
|
||
| var launchCommand = new GodotLaunchCommand(_context.Object); | ||
| await launchCommand.ExecuteAsync(_console); | ||
|
|
||
| _processRunner.Verify(p => | ||
| p.RunDetached(It.IsAny<string>(), It.IsAny<string[]>()), Times.Never | ||
| ); | ||
|
|
||
| _log.ToString().ShouldContain($"The GODOT environment variable points to a missing file: {godotPath}"); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,47 @@ | ||||||||||
| namespace Chickensoft.GodotEnv.Features.Godot.Commands; | ||||||||||
|
|
||||||||||
| using System; | ||||||||||
| using System.IO; | ||||||||||
| using System.Threading.Tasks; | ||||||||||
| using Chickensoft.GodotEnv.Common.Clients; | ||||||||||
| using Chickensoft.GodotEnv.Common.Models; | ||||||||||
| using CliFx; | ||||||||||
| using CliFx.Attributes; | ||||||||||
| using CliFx.Infrastructure; | ||||||||||
| using global::GodotEnv.Common.Utilities; | ||||||||||
|
|
||||||||||
| [Command("godot launch", Description = "Launches the currently active Godot version.")] | ||||||||||
| public class GodotLaunchCommand : ICommand, ICliCommand { | ||||||||||
| public IExecutionContext ExecutionContext { get; set; } = default!; | ||||||||||
|
|
||||||||||
| public GodotLaunchCommand(IExecutionContext context) { | ||||||||||
| ExecutionContext = context; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private ISystemInfo SystemInfo => ExecutionContext.Godot.Platform.SystemInfo; | ||||||||||
|
|
||||||||||
|
|
||||||||||
| public async ValueTask ExecuteAsync(IConsole console) { | ||||||||||
| var log = ExecutionContext.CreateLog(console); | ||||||||||
|
|
||||||||||
| string? godotPath = Environment.GetEnvironmentVariable("GODOT"); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this, I would suggest we wait til #101 is merged and then do:
Suggested change
And then update tests to match. (The reason to wait on #101 is that
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this change, why should we reference the SymLink and not the environment variable directly? It was my understanding after speaking with @jolexxa that we should reference off of the EnvVariable?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, okay. Wait for @jolexxa to weigh in, but in terms of my rationale:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @TheDevRatt what Mark said, essentially. The env var is for the user to launch Godot via the symlink and for use in vscode launch configs or any other scripts that need to point to Godot. Since we are managing the symlink, we can just access it directly. |
||||||||||
|
|
||||||||||
| if (string.IsNullOrWhiteSpace(godotPath)) { | ||||||||||
| log.Err("❌ The GODOT environment variable is not set."); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think (not 100% sure) that @jolexxa has "removing emoji from the output" on her roadmap for GodotEnv, so we may not want to add new ones. Maybe she can confirm if I'm remembering that correctly, though. |
||||||||||
| log.Print("To set it, use:\n godotenv godot use <version>\n"); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (SystemInfo.OS == OSType.Windows && !godotPath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) { | ||||||||||
| godotPath += ".exe"; | ||||||||||
| } | ||||||||||
|
Comment on lines
+35
to
+37
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here again I'd suggest waiting for #101 - this will also be handled by |
||||||||||
|
|
||||||||||
| if (!File.Exists(godotPath)) { | ||||||||||
| log.Err($"❌ The GODOT environment variable points to a missing file: {godotPath}"); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| log.Print($"🚀 Launching Godot from {godotPath}..."); | ||||||||||
| await ExecutionContext.Godot.GodotRepo.ProcessRunner.RunDetached(godotPath, []); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This suggestion will require implementing |
||||||||||
| } | ||||||||||
| } | ||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test names in Chickensoft projects are usually just PascalCase, with no underscores.