diff --git a/CHANGELOG.md b/CHANGELOG.md index db73cfa..29b3ffb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.3.2] - 2026-07-30 + +### Fixed + +- Removed the hidden `ICommand`-to-`TBaseCommand` downcast during execution. + Commands that implement `ICommand` directly can now execute without an + invalid type-cast failure. +- Added the optional `ICommandParameterReceiver` capability used by + `TBaseCommand` to receive framework-parsed parameter values without making + it a requirement of the base `ICommand` contract. + +### Deprecated + +- Marked `TCLIApplication.RegisterFlagValueCompletion()` and + `RegisterPositionalCompletion()` as deprecated. These methods remain + source-compatible no-op stubs for the 1.x line and are planned for removal + in v2.0.0. + +### Changed + +- Added regression coverage for executing a command that implements + `ICommand` without inheriting from `TBaseCommand`. +- Updated the README badge and Lazarus package metadata to `1.3.2`. + ## [1.3.1] - 2026-07-28 ### Documentation @@ -353,7 +377,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - README with quick start guide - System requirements and compatibility information -[Unreleased]: https://github.com/ikelaiah/cli-fp/compare/v1.3.1...HEAD +[Unreleased]: https://github.com/ikelaiah/cli-fp/compare/v1.3.2...HEAD +[1.3.2]: https://github.com/ikelaiah/cli-fp/compare/v1.3.1...v1.3.2 [1.3.1]: https://github.com/ikelaiah/cli-fp/compare/v1.3.0...v1.3.1 [1.3.0]: https://github.com/ikelaiah/cli-fp/compare/v1.2.0...v1.3.0 [1.2.0]: https://github.com/ikelaiah/cli-fp/compare/v1.1.6...v1.2.0 diff --git a/README.md b/README.md index 21669dd..70a568b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Command-Line Interface Framework for Free Pascal 🚀 [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Version](https://img.shields.io/badge/version-1.3.1-blue.svg)](https://github.com/ikelaiah/cli-fp/releases) +[![Version](https://img.shields.io/badge/version-1.3.2-blue.svg)](https://github.com/ikelaiah/cli-fp/releases) [![Free Pascal](https://img.shields.io/badge/Free%20Pascal-3.2.2-blue.svg)](https://www.freepascal.org/) [![Lazarus](https://img.shields.io/badge/Lazarus-4.0-orange.svg)](https://www.lazarus-ide.org/) [![GitHub stars](https://img.shields.io/github/stars/ikelaiah/cli-fp?style=social)](https://github.com/ikelaiah/cli-fp/stargazers) diff --git a/ROADMAP.md b/ROADMAP.md index 5692c8f..241a6b7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -5,7 +5,7 @@ lower maintenance cost. New convenience APIs should delegate to one underlying implementation rather than creating parallel parsing, validation, or execution paths. -## v1.3.2 — Correct the Current Contracts +## v1.3.2 — Correct the Current Contracts (completed 2026-07-30) - Remove or clearly deprecate the non-functional completion callback APIs. - Fix the mismatch between the public `ICommand` contract and the runtime @@ -46,4 +46,3 @@ can be made and tested independently. **Maintenance outcome:** command inputs and ownership are explicit, legacy compatibility paths are retired, and the core has one coherent execution model. - diff --git a/docs/PULL_REQUEST_v1.3.2.md b/docs/PULL_REQUEST_v1.3.2.md new file mode 100644 index 0000000..a17993e --- /dev/null +++ b/docs/PULL_REQUEST_v1.3.2.md @@ -0,0 +1,92 @@ +# Pull Request: Release v1.3.2 - Contract Hardening + +**Target Release:** v1.3.2 + +**Release Date:** 2026-07-30 + +## Summary + +This PR delivers the `v1.3.2` roadmap milestone by correcting the mismatch +between the public `ICommand` contract and the runtime implementation, and by +clearly deprecating the non-functional custom-completion callback methods. + +It also adds the maintenance roadmap, regression coverage, current +documentation, release notes, and version metadata for the patch release. + +## Type of Change + +- [x] Bug fix +- [x] Backward-compatible interface addition +- [x] API deprecation +- [x] Regression test +- [x] Documentation +- [ ] Breaking change + +## Contract Correction + +- [x] Add the optional `ICommandParameterReceiver` capability. +- [x] Make `TBaseCommand` implement the receiver. +- [x] Replace the unconditional `ICommand`-to-`TBaseCommand` downcast with a + `Supports()` capability check. +- [x] Remove the `CLI.Command` implementation dependency from + `CLI.Application`. +- [x] Verify that an independent `ICommand` implementation executes and + returns its exit code. + +## Completion Callback Deprecation + +- [x] Mark `RegisterFlagValueCompletion()` as deprecated. +- [x] Mark `RegisterPositionalCompletion()` as deprecated. +- [x] Retain both methods as no-ops for 1.x source compatibility. +- [x] State that removal is planned for v2.0.0. +- [x] Confirm that built-in metadata completion is unaffected. + +## Documentation and Versioning + +- [x] Add `ROADMAP.md` with maintenance outcomes through v2.0.0. +- [x] Update the API reference for `ICommandParameterReceiver`. +- [x] Update the technical completion notes and architecture description. +- [x] Add the dated `1.3.2` changelog entry and comparison links. +- [x] Update the README badge to `1.3.2`. +- [x] Update Lazarus package metadata to `1.3.2`. +- [x] Add v1.3.2 release notes. +- [x] Add this pull request note. + +## Compatibility + +Existing `TBaseCommand` descendants require no changes. Existing application +factories, command registration calls, generated projects, and schema-version-1 +manifests retain their behaviour. + +Direct `ICommand` implementations no longer fail merely because they do not +inherit from `TBaseCommand`. The new receiver interface is optional. + +The deprecated custom-completion methods remain callable in 1.x but can emit +compiler warnings. Removing calls to them is behaviour-preserving because the +methods were already no-ops. + +## Verification + +- [x] Framework suite: 39 tests, 0 errors, 0 failures. +- [x] Interface-only command regression test. +- [x] Deprecation compiler-warning smoke test. +- [x] Windows generator unit tests. +- [x] Generator golden-output test. +- [x] Generated-project compile smoke test. +- [x] Generator operations test. +- [x] All seven example applications compiled. +- [x] Lazarus runtime package build with metadata at `1.3.2`. +- [x] Local Markdown targets and fenced code blocks across 39 Markdown files. +- [x] FPC 3.2.2. +- [ ] GitHub Actions after the PR is opened. + +## Release Readiness + +- [x] Release date finalized as 2026-07-30. +- [x] Version metadata updated to `1.3.2`. +- [x] Changelog section dated and comparison links updated. +- [x] Release notes prepared. +- [x] Pull request notes prepared. +- [ ] Confirm GitHub Actions on Windows and Linux. + +After merge, create the `v1.3.2` tag and publish the prepared release notes. diff --git a/docs/RELEASE_NOTES_v1.3.2.md b/docs/RELEASE_NOTES_v1.3.2.md new file mode 100644 index 0000000..2b671f9 --- /dev/null +++ b/docs/RELEASE_NOTES_v1.3.2.md @@ -0,0 +1,94 @@ +# Release Notes - cli-fp v1.3.2 + +**Release Date:** 2026-07-30 + +## Overview + +Version `1.3.2` is a contract-hardening patch release. It removes a hidden +runtime dependency on `TBaseCommand`, formally deprecates the non-functional +custom-completion callback registration methods, and establishes the +maintenance roadmap for the next releases. + +Existing applications based on `TBaseCommand` remain source-compatible. + +## `ICommand` Contract Fix + +Earlier versions accepted commands through the `ICommand` interface but cast +the selected command to `TBaseCommand` before execution. A valid independent +implementation of `ICommand` could therefore register successfully and then +fail with an invalid type cast at runtime. + +Version `1.3.2` removes that downcast. Parameter handoff now uses the optional +`ICommandParameterReceiver` capability: + +- `TBaseCommand` implements `ICommandParameterReceiver`, preserving existing + parameter lookup behaviour. +- Commands that implement only `ICommand` can execute without inheriting from + `TBaseCommand`. +- Commands that need framework-managed parameter lookup can derive from + `TBaseCommand` or implement the optional receiver contract. + +The regression suite now includes an interface-only command and verifies its +execution and exit code. + +## Deprecated Completion Callback APIs + +The following concrete `TCLIApplication` methods are now marked deprecated: + +- `RegisterFlagValueCompletion()` +- `RegisterPositionalCompletion()` + +These methods have always been non-functional stubs. They remain as no-ops in +the 1.x line for source compatibility and are planned for removal in v2.0.0. + +Built-in completion is unchanged. Registered commands, subcommands, flags, +Boolean values, and enum values continue to be completed from command +metadata. + +## Maintenance Roadmap + +The new project roadmap records the intended sequence: + +- `v1.3.2`: correct current contracts; +- `v1.4.0`: add a simple callback API and typed argument access; +- `v1.5.0`: split help and completion out of `TCLIApplication`; and +- `v2.0.0`: adopt an explicit execution context and retire legacy shared-state + plumbing. + +Convenience APIs are expected to delegate to one underlying implementation so +that improved ergonomics do not create parallel parsing or validation paths. + +## Verification + +- Framework suite: 39 tests, 0 errors, 0 failures. +- Added regression coverage for an `ICommand` implementation that does not + inherit from `TBaseCommand`. +- Verified that use of a deprecated callback registration method produces the + intended FPC compiler warning. +- Full Windows generator suite passed: + - generator unit tests; + - golden-output test; + - generated-project compile smoke test; and + - generator operations test. +- All seven example applications compiled successfully. +- Lazarus runtime package compiled with version metadata at `1.3.2`. +- Local Markdown targets resolve and fenced code blocks are balanced across 39 + Markdown files. +- FPC version: 3.2.2. + +## Compatibility + +No migration is required for applications using `TBaseCommand`, +`CreateCLIApplication`, existing command registration, schema-version-1 +`clifp.json` files, or generated projects. + +Applications that call either deprecated custom-completion registration method +may receive a compiler warning. Removing those calls does not change runtime +behaviour because the methods did not register callbacks. + +## Versioning + +- The README release badge now targets `1.3.2`. +- The Lazarus package metadata now targets `1.3.2`. + +**Full Changelog:** [v1.3.1...v1.3.2](https://github.com/ikelaiah/cli-fp/compare/v1.3.1...v1.3.2) diff --git a/docs/api-reference.md b/docs/api-reference.md index 3a72280..dcb27ba 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -9,8 +9,8 @@ with the [user manual](user-manual.md); it supplies complete program context that the isolated declarations here intentionally omit. The excerpts focus on supported application-facing members. Test hooks on -`TCLIApplication` and the currently disabled custom-completion callback -registry are documented in the +`TCLIApplication` and the deprecated, non-functional custom-completion +callback registry are documented in the [technical completion notes](technical-docs.md#historical-investigation-disabled-custom-callbacks). The declarations in [`src/`](../src/) remain authoritative. @@ -77,6 +77,19 @@ ICommand = interface end; ``` +##### `ICommandParameterReceiver` + +Optional capability used by the application to supply parsed parameter values +to commands that want framework-managed lookup. `TBaseCommand` implements this +interface. A command that implements only `ICommand` remains valid and can +execute without inheriting from `TBaseCommand`. + +```pascal +ICommandParameterReceiver = interface + procedure SetParsedParams(const Params: TStringList); +end; +``` + ### Parameter Helper Methods The `TBaseCommand` class provides helper methods for adding parameters. All helper methods are available on `TBaseCommand` and its descendants: @@ -342,7 +355,7 @@ Base command implementation. ##### `TBaseCommand` Abstract base class for all CLI commands. ```pascal -TBaseCommand = class(TInterfacedObject, ICommand) +TBaseCommand = class(TInterfacedObject, ICommand, ICommandParameterReceiver) protected function GetParameterValue(const Flag: string; out Value: string): Boolean; procedure ShowHelp; diff --git a/docs/technical-docs.md b/docs/technical-docs.md index 4d3b127..a523bc6 100644 --- a/docs/technical-docs.md +++ b/docs/technical-docs.md @@ -28,6 +28,11 @@ classDiagram +GetSubCommands(): TArray +Execute(): Integer } + + class ICommandParameterReceiver { + <> + +SetParsedParams(Params: TStringList) + } class ICommandParameter { <> @@ -134,6 +139,7 @@ classDiagram ICLIApplication <|.. TCLIApplication ICommand <|.. TBaseCommand + ICommandParameterReceiver <|.. TBaseCommand ICommandParameter <|.. TCommandParameter IProgressIndicator <|.. TProgressIndicator TProgressIndicator <|-- TProgressBar @@ -164,6 +170,20 @@ ICommand = interface end; ``` +#### `ICommandParameterReceiver` + +`ICommandParameterReceiver` is an optional capability, not a requirement for +all commands. Before validation and execution, `TCLIApplication` uses +`Supports()` to provide its parsed parameter list when the selected command +implements this interface. `TBaseCommand` implements it; a standalone +`ICommand` that does not need framework-managed lookup can execute without it. + +```pascal +ICommandParameterReceiver = interface + procedure SetParsedParams(const Params: TStringList); +end; +``` + #### `ICommandParameter` ```pascal ICommandParameter = interface @@ -245,7 +265,7 @@ parsing, validation, and exception handling pipeline. #### `TBaseCommand` (`CLI.Command`) Base implementation for commands with: ```pascal -TBaseCommand = class(TInterfacedObject, ICommand) +TBaseCommand = class(TInterfacedObject, ICommand, ICommandParameterReceiver) private FName: string; FDescription: string; @@ -756,7 +776,7 @@ Return: suggestions + ":4" (CD_NOFILE) ### Completion Feature Matrix The completion system provides built-in static completion; the exposed custom -callback methods are currently disabled: +callback methods are deprecated and non-functional: | Feature | Status | Implementation Details | |---------|--------|----------------------| @@ -765,7 +785,7 @@ callback methods are currently disabled: | **Flags (short/long)** | ✅ Fully functional | Context-aware at each command level | | **Boolean values** | ✅ Fully functional | Auto-completes with `true`/`false` | | **Enum values** | ✅ Fully functional | Auto-completes with allowed values | -| **Custom callbacks** | ⏳ Disabled | Public registration methods are currently stubs | +| **Custom callbacks** | ⚠️ Deprecated | Registration methods are non-functional stubs and are planned for removal in v2.0.0 | **Implementation Approach:** @@ -774,10 +794,11 @@ callback methods are currently disabled: PowerShell functions call the hidden `__complete` entrypoint; no callback registry is needed for command, flag, boolean, or enum candidates. -- **Custom callbacks** (⏳ Disabled): The concrete application class exposes - `RegisterFlagValueCompletion()` and `RegisterPositionalCompletion()`, but the - current methods are stubs. The historical investigation below explains why - the earlier storage approach was not enabled. +- **Custom callbacks** (⚠️ Deprecated): The concrete application class exposes + `RegisterFlagValueCompletion()` and `RegisterPositionalCompletion()` only for + 1.x source compatibility. The methods are marked deprecated, remain no-op + stubs, and are planned for removal in v2.0.0. The historical investigation + below explains why the earlier storage approach was not enabled. Built-in completion covers registered commands, flags, Boolean values, and enum values. It cannot currently obtain dynamic candidates from a filesystem, @@ -787,11 +808,12 @@ database, API, or other application callback. ### Historical Investigation: Disabled Custom Callbacks -> **Current authoritative status:** The public methods exist on -> `TCLIApplication`, but their bodies are stubs and always perform no -> registration. The discussion below records the earlier implementation -> investigation; it is not a general claim that all procedural values in FPC -> dynamic arrays are unsupported. +> **Current authoritative status:** The deprecated public methods remain on +> `TCLIApplication` for 1.x source compatibility, but their bodies are stubs +> and always perform no registration. They are planned for removal in v2.0.0. +> The discussion below records the earlier implementation investigation; it +> is not a general claim that all procedural values in FPC dynamic arrays are +> unsupported. #### The Problem @@ -907,18 +929,21 @@ Only advanced scenarios requiring **runtime-dynamic** completions from external #### Code Location -The stubbed methods can be found by name in `src/cli.application.pas`: +The deprecated public stubs and their private lookup helpers can be found by +name in `src/cli.application.pas`: - `RegisterFlagValueCompletion()` - `RegisterPositionalCompletion()` - `GetRegisteredFlagCompletion()` - `GetRegisteredPositionalCompletion()` -All contain TODO comments: `"Implement when FPC function pointer storage is resolved"` +The public registration methods are deprecated no-ops. The private lookup +helpers retain TODO markers because no callback registry is active. Before enabling the callback API, re-evaluate the design against the project's supported compiler and retain regression coverage for callback lifetime and retrieval. -**Bottom line:** Custom callbacks are disabled in the current implementation. -Built-in command, flag, Boolean, and enum completion does not depend on them. +**Bottom line:** Custom callbacks are deprecated and non-functional in the +current implementation. Built-in command, flag, Boolean, and enum completion +does not depend on them. diff --git a/packages/lazarus/cli_fp.lpk b/packages/lazarus/cli_fp.lpk index 1e62a3d..8463bb9 100644 --- a/packages/lazarus/cli_fp.lpk +++ b/packages/lazarus/cli_fp.lpk @@ -36,7 +36,7 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. "/> - + diff --git a/src/cli.application.pas b/src/cli.application.pas index 4f00805..0695782 100644 --- a/src/cli.application.pas +++ b/src/cli.application.pas @@ -182,9 +182,14 @@ TCLIApplication = class(TInterfacedObject, ICLIApplication) property ParsedParams: TStringList read FParsedParams; property CurrentCommand: ICommand read FCurrentCommand write FCurrentCommand; - { Register completion callbacks (opt-in) } - procedure RegisterFlagValueCompletion(const CommandPath, FlagName: string; Func: TFlagValueCompletionFunc); - procedure RegisterPositionalCompletion(const CommandPath: string; ArgIndex: Integer; Func: TPositionalCompletionFunc); + { Deprecated no-op callback registration retained for 1.x source + compatibility. Built-in metadata completion is unaffected. } + procedure RegisterFlagValueCompletion(const CommandPath, FlagName: string; + Func: TFlagValueCompletionFunc); + deprecated 'Custom completion callbacks are non-functional and will be removed in v2.0.0'; + procedure RegisterPositionalCompletion(const CommandPath: string; + ArgIndex: Integer; Func: TPositionalCompletionFunc); + deprecated 'Custom completion callbacks are non-functional and will be removed in v2.0.0'; { For testing validation } function TestValidateCommand: Boolean; @@ -217,7 +222,7 @@ function CreateCLIApplication(const Name, Version: string; implementation uses - StrUtils, CLI.Command, CLI.Console; + StrUtils, CLI.Console; { Constructor: Initializes a new CLI application instance @param AName The name of the application @@ -488,12 +493,12 @@ function TCLIApplication.ExecuteArguments: Integer; { Parses, validates, and executes the selected command. } function TCLIApplication.ExecuteCurrentCommand: Integer; var - Command: TBaseCommand; + ParameterReceiver: ICommandParameterReceiver; begin ParseCommandLine; - Command := FCurrentCommand as TBaseCommand; - Command.SetParsedParams(FParsedParams); + if Supports(FCurrentCommand, ICommandParameterReceiver, ParameterReceiver) then + ParameterReceiver.SetParsedParams(FParsedParams); if not ValidateCommand then Exit(1); @@ -1182,15 +1187,13 @@ function TCLIApplication.TestValidateCommand: Boolean; { RegisterFlagValueCompletion: Register a callback for flag value completion } procedure TCLIApplication.RegisterFlagValueCompletion(const CommandPath, FlagName: string; Func: TFlagValueCompletionFunc); begin - // TODO: Implement when FPC function pointer storage is resolved - // Currently disabled due to FPC compatibility issues + // Deprecated no-op retained for 1.x source compatibility. end; { RegisterPositionalCompletion: Register a callback for positional argument completion } procedure TCLIApplication.RegisterPositionalCompletion(const CommandPath: string; ArgIndex: Integer; Func: TPositionalCompletionFunc); begin - // TODO: Implement when FPC function pointer storage is resolved - // Currently disabled due to FPC compatibility issues + // Deprecated no-op retained for 1.x source compatibility. end; { GetRegisteredFlagCompletion: Helper to retrieve flag completion callback } diff --git a/src/cli.command.pas b/src/cli.command.pas index a641653..37c9083 100644 --- a/src/cli.command.pas +++ b/src/cli.command.pas @@ -18,7 +18,7 @@ interface - Subcommand support - Help text generation - Parameter value access } - TBaseCommand = class(TInterfacedObject, ICommand) + TBaseCommand = class(TInterfacedObject, ICommand, ICommandParameterReceiver) private FName: string; // Command name used in CLI FDescription: string; // Command description for help diff --git a/src/cli.interfaces.pas b/src/cli.interfaces.pas index 539e3a0..88bc5a8 100644 --- a/src/cli.interfaces.pas +++ b/src/cli.interfaces.pas @@ -24,6 +24,7 @@ interface { Forward declarations for circular references } ICommand = interface; + ICommandParameterReceiver = interface; ICommandParameter = interface; ICLIApplication = interface; IProgressIndicator = interface; @@ -62,6 +63,14 @@ interface property SubCommands: specialize TArray read GetSubCommands; end; + { Optional command capability used to receive parameters parsed by the + application. Commands that do not need framework-managed parameter access + can implement ICommand without implementing this interface. } + ICommandParameterReceiver = interface + ['{D6F6D6D3-5C5C-4B5B-9B5B-5B5B5B5B5B5B}'] + procedure SetParsedParams(const Params: TStringList); + end; + { Command parameter interface - Represents a single command-line parameter } ICommandParameter = interface ['{D6F6D6D1-5C5C-4B5B-9B5B-5B5B5B5B5B5B}'] diff --git a/tests/testcase.pas b/tests/testcase.pas index ec17790..6664187 100644 --- a/tests/testcase.pas +++ b/tests/testcase.pas @@ -32,6 +32,7 @@ TCLIFrameworkTests = class(TTestCase) procedure Test_2_3_SubCommands; procedure Test_2_4_CommandExecution; procedure Test_2_5_CommandHierarchy; + procedure Test_2_6_InterfaceOnlyCommandExecution; // 3.x - Parameter Tests procedure Test_3_1_CreateParameter; @@ -92,6 +93,19 @@ TRecordingCommand = class(TBaseCommand) property LastName: string read FLastName; end; + { ICommand implementation that deliberately does not inherit TBaseCommand. } + TInterfaceOnlyCommand = class(TInterfacedObject, ICommand) + private + FExecuteCount: Integer; + public + function GetName: string; + function GetDescription: string; + function GetParameters: specialize TArray; + function GetSubCommands: specialize TArray; + function Execute: Integer; + property ExecuteCount: Integer read FExecuteCount; + end; + function TTestCommand.Execute: Integer; begin Result := 0; @@ -110,6 +124,32 @@ function TRecordingCommand.Execute: Integer; Result := 0; end; +function TInterfaceOnlyCommand.GetName: string; +begin + Result := 'standalone'; +end; + +function TInterfaceOnlyCommand.GetDescription: string; +begin + Result := 'Interface-only command'; +end; + +function TInterfaceOnlyCommand.GetParameters: specialize TArray; +begin + Result := nil; +end; + +function TInterfaceOnlyCommand.GetSubCommands: specialize TArray; +begin + Result := nil; +end; + +function TInterfaceOnlyCommand.Execute: Integer; +begin + Inc(FExecuteCount); + Result := 23; +end; + function MakeArgs(const Values: array of string): TStringArray; var i: Integer; @@ -261,6 +301,27 @@ procedure TCLIFrameworkTests.Test_2_5_CommandHierarchy; end; end; +procedure TCLIFrameworkTests.Test_2_6_InterfaceOnlyCommandExecution; +var + App: TCLIApplication; + Cmd: TInterfaceOnlyCommand; + Command: ICommand; +begin + App := TCLIApplication.Create('TestApp', '1.3.2'); + Cmd := TInterfaceOnlyCommand.Create; + Command := Cmd; + try + App.RegisterCommand(Command); + AssertEquals('Interface-only command exit code should be returned', 23, + App.TestExecute(MakeArgs(['standalone']))); + AssertEquals('Interface-only command should execute once', 1, + Cmd.ExecuteCount); + finally + App.Free; + Command := nil; + end; +end; + // 3.x - Parameter Tests procedure TCLIFrameworkTests.Test_3_1_CreateParameter;