File tree Expand file tree Collapse file tree 3 files changed +78
-0
lines changed
test/CommandLineUtils.Tests Expand file tree Collapse file tree 3 files changed +78
-0
lines changed Original file line number Diff line number Diff line change 11// Copyright (c) Nate McMaster.
22// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
4+ using McMaster . Extensions . CommandLineUtils . Abstractions ;
5+ using McMaster . Extensions . CommandLineUtils . Errors ;
46using System ;
57using System . Linq ;
68using System . Reflection ;
@@ -28,6 +30,8 @@ public virtual void Apply(ConventionContext context)
2830 var contextArgs = new object [ ] { context , attribute } ;
2931 foreach ( var type in attribute . Types )
3032 {
33+ AssertSubcommandIsNotCycled ( type , context . Application ) ;
34+
3135 var impl = s_addSubcommandMethod . MakeGenericMethod ( type ) ;
3236 try
3337 {
@@ -42,6 +46,19 @@ public virtual void Apply(ConventionContext context)
4246 }
4347 }
4448
49+ private void AssertSubcommandIsNotCycled ( Type modelType , CommandLineApplication parentCommand )
50+ {
51+ while ( parentCommand != null )
52+ {
53+ if ( parentCommand is IModelAccessor parentCommandAccessor
54+ && parentCommandAccessor . GetModelType ( ) == modelType )
55+ {
56+ throw new SubcommandCycleException ( modelType ) ;
57+ }
58+ parentCommand = parentCommand . Parent ;
59+ }
60+ }
61+
4562 private static readonly MethodInfo s_addSubcommandMethod
4663 = typeof ( SubcommandAttributeConvention ) . GetRuntimeMethods ( )
4764 . Single ( m => m . Name == nameof ( AddSubcommandImpl ) ) ;
Original file line number Diff line number Diff line change 1+ // Copyright (c) Nate McMaster.
2+ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+ using System ;
5+
6+ namespace McMaster . Extensions . CommandLineUtils . Errors
7+ {
8+ /// <summary>
9+ /// The exception that is thrown when a subcommand cycle is detected
10+ /// </summary>
11+ public class SubcommandCycleException : Exception
12+ {
13+ /// <summary>
14+ /// Initializes an instance of <see cref="SubcommandCycleException"/>.
15+ /// </summary>
16+ /// <param name="modelType">The type of the cycled command model</param>
17+ public SubcommandCycleException ( Type modelType )
18+ : base ( $ "Subcommand cycle detected: trying to add command of model { modelType } as its own direct or indirect subcommand")
19+ {
20+ ModelType = modelType ;
21+ }
22+
23+ /// <summary>
24+ /// The type of the cycled command model
25+ /// </summary>
26+ public Type ModelType { get ; }
27+ }
28+ }
Original file line number Diff line number Diff line change 11// Copyright (c) Nate McMaster.
22// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
4+ using McMaster . Extensions . CommandLineUtils . Errors ;
45using System ;
56using System . IO ;
67using System . Linq ;
@@ -198,5 +199,37 @@ public void CommandNamesCannotDifferByCaseOnly()
198199 ( ) => CommandLineApplication . Execute < DuplicateSubCommands > ( new TestConsole ( _output ) ) ) ;
199200 Assert . Equal ( Strings . DuplicateSubcommandName ( "level1" ) , ex . Message ) ;
200201 }
202+
203+ [ Command , Subcommand ( typeof ( CycledCommand2 ) ) ]
204+ private class CycledCommand1
205+ {
206+ }
207+
208+ [ Command , Subcommand ( typeof ( CycledCommand1 ) ) ]
209+ private class CycledCommand2
210+ {
211+ }
212+
213+ [ Fact ]
214+ public void ThrowsForCycledSubCommand ( )
215+ {
216+ var ex = Assert . Throws < SubcommandCycleException > (
217+ ( ) => CommandLineApplication . Execute < CycledCommand1 > ( new TestConsole ( _output ) ) ) ;
218+ Assert . Equal ( typeof ( CycledCommand1 ) , ex . ModelType ) ;
219+ }
220+
221+ [ Command , Subcommand ( typeof ( SelfCycledCommand ) ) ]
222+ private class SelfCycledCommand
223+ {
224+
225+ }
226+
227+ [ Fact ]
228+ public void ThrowsForSelfCycledCommand ( )
229+ {
230+ var ex = Assert . Throws < SubcommandCycleException > (
231+ ( ) => CommandLineApplication . Execute < SelfCycledCommand > ( new TestConsole ( _output ) ) ) ;
232+ Assert . Equal ( typeof ( SelfCycledCommand ) , ex . ModelType ) ;
233+ }
201234 }
202235}
You can’t perform that action at this time.
0 commit comments