File: nested.t

package info (click to toggle)
remctl 3.17-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 5,348 kB
  • sloc: ansic: 19,365; sh: 5,203; perl: 1,767; java: 740; makefile: 715; xml: 501; python: 431
file content (185 lines) | stat: -rwxr-xr-x 6,286 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/usr/bin/perl
#
# Tests for nested commands in Net::Remctl::Backend.
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2020 Russ Allbery <eagle@eyrie.org>
# Copyright 2013
#     The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: MIT

use 5.008;
use strict;
use warnings;

use lib 't/lib';

use Test::More tests => 35;
use Test::Remctl qw(run_wrapper);

# Load the module.
BEGIN { use_ok('Net::Remctl::Backend') }

# Index of the sub name in the list output of caller.
use constant CALLER_SUB => 3;

# We'll use this variable to accumulate call sequences to test that the right
# functions were called by the backend wrapper.  Each element will be a
# reference to an array holding the called function and any arguments.
my @CALLS;

# Helper function for test commands to save arguments in the call stack.
#
# Returns: undef
sub save_args {
    my (@args) = @_;
    my $caller = (caller(1))[CALLER_SUB];
    push(@CALLS, [$caller, @args]);
    return;
}

# Set up a couple of test commands.
sub test_cmd1 { my (@args) = @_; save_args(@args); return 1 }
sub test_cmd2 { my (@args) = @_; save_args(@args); return 2 }

# Set up nested command dispatch.  The first command has a syntax that's
# exactly 48 characters long, once the prefix is added, which tests an edge
# case for help formatting.
my %commands = (
    command => {
        code    => \&test_cmd1,
        summary => 'it is nothing but args!',
        syntax  => 'arg arg arg arg arg arg arg',
    },
    nest => {
        code    => \&test_cmd1,
        summary => 'the nested command with no subcommand',
        syntax  => q{},
        nested  => {
            cmd1 => {
                code   => \&test_cmd1,
                syntax => 'arg1 [arg2]',
            },
            cmd2 => {
                code    => \&test_cmd2,
                summary => 'apply cmd1 to the nest of arg1',
                syntax  => q{},
            },
            nest => {
                nested => {
                    bar => {
                        code     => \&test_cmd1,
                        args_min => 1,
                    },
                    foo => {
                        code    => \&test_cmd2,
                        summary => 'even more nesting!',
                        syntax  => 'arg1',
                    },
                },
            },
        },
    },
);
my %args = (
    command     => 'frobnicate',
    commands    => \%commands,
    help_banner => 'Frobnicate remctl help:',
);
my $backend = Net::Remctl::Backend->new(\%args);
isa_ok($backend, 'Net::Remctl::Backend');

# Try running the nested commands and check the result.
my ($out, $err, $status) = run_wrapper($backend, qw(nest cmd1 foo bar));
is($status, 1,   'cmd1 returns correct status');
is($out,    q{}, '... and no output');
is($err,    q{}, '... and no errors');
is_deeply(\@CALLS, [[qw(main::test_cmd1 foo bar)]], 'cmd1 called correctly');
@CALLS = ();
($out, $err, $status) = run_wrapper($backend, qw(nest cmd2));
is($status, 2,   'cmd2 returns correct status');
is($out,    q{}, '... and no output');
is($err,    q{}, '... and no errors');
is_deeply(\@CALLS, [[qw(main::test_cmd2)]], 'cmd2 called correctly');
@CALLS = ();

# Ensure there's nothing weird about the regular command.
($out, $err, $status) = run_wrapper($backend, qw(command foo bar));
is($status, 1,   'cmd1 returns correct status');
is($out,    q{}, '... and no output');
is($err,    q{}, '... and no errors');
is_deeply(\@CALLS, [[qw(main::test_cmd1 foo bar)]], 'cmd1 called correctly');
@CALLS = ();

# Ensure we can call the nested command itself.
($out, $err, $status) = run_wrapper($backend, qw(nest));
is($status, 1,   'nest returns correct status');
is($out,    q{}, '... and no output');
is($err,    q{}, '... and no errors');
is_deeply(\@CALLS, [[qw(main::test_cmd1)]], 'nest called correctly');
@CALLS = ();

# Try the double-nested commands.
($out, $err, $status) = run_wrapper($backend, qw(nest nest bar foo));
is($status, 1,   'nest nest bar returns correct status');
is($out,    q{}, '... and no output');
is($err,    q{}, '... and no errors');
is_deeply(\@CALLS, [[qw(main::test_cmd1 foo)]], 'bar called correctly');
@CALLS = ();
($out, $err, $status) = run_wrapper($backend, qw(nest nest foo));
is($status, 2,   'nest nest foo returns correct status');
is($out,    q{}, '... and no output');
is($err,    q{}, '... and no errors');
is_deeply(\@CALLS, [[qw(main::test_cmd2)]], 'bar called correctly');
@CALLS = ();

# Ensure an unknown nested command returns the right error.
{
    local @ARGV = qw(nest unknown);
    $status = eval { $backend->run };
}
is($status, undef, 'run() of unknown command returns undef');
is($@, "Unknown command nest unknown\n", '...with correct error');

# Ensure an unknown double-nested command returns the right error.
{
    local @ARGV = qw(nest nest unknown);
    $status = eval { $backend->run };
}
is($status, undef, 'run() of unknown command returns undef');
is($@, "Unknown command nest nest unknown\n", '...with correct error');

# Verify argument checking is handled properly in nested commands.
{
    local @ARGV = qw(nest nest bar);
    $status = eval { $backend->run };
}
is($status, undef, 'run() with insufficient arguments returns undef');
is(
    $@,
    "frobnicate nest nest bar: insufficient arguments\n",
    '...with correct error',
);

# Check the help output.
my $expected = <<'END_HELP';
Frobnicate remctl help:
  frobnicate command arg arg arg arg arg arg arg  it is nothing but args!
  frobnicate nest                                 the nested command with no
                                                  subcommand
  frobnicate nest cmd1 arg1 [arg2]
  frobnicate nest cmd2                            apply cmd1 to the nest of
                                                  arg1
  frobnicate nest nest foo arg1                   even more nesting!
END_HELP
is($backend->help, $expected, 'Help output is correct');

# If we remove the code parameter for the nest command, it's now unknown.
delete $commands{nest}{code};
{
    local @ARGV = qw(nest);
    $status = eval { $backend->run };
}
is($status, undef,                    'run() of nest command returns undef');
is($@,      "Unknown command nest\n", '...with correct error');