1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * Copyright (C) 2020-2021 The DOSBox Staging Team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21 /* This sample shows how to write a simple unit test for dosbox-staging using
22 * Google C++ testing framework.
23 *
24 * Read Google Test Primer for reference of most available features, macros,
25 * and guidance about writing unit tests:
26 *
27 * https://github.com/google/googletest/blob/master/googletest/docs/primer.md#googletest-primer
28 */
29
30 /* Include necessary header files; order of headers should be as follows:
31 *
32 * 1. Header declaring functions/classes being tested
33 * 2. <gtest/gtest.h>, which declares the testing framework
34 * 3. Additional system headers (if needed)
35 * 4. Additional dosbox-staging headers (if needed)
36 */
37
38 #include "shell.h"
39
40 #include <string>
41
42 #include <gmock/gmock.h>
43 #include <gtest/gtest.h>
44
45 #include "dosbox_test_fixture.h"
46 #include "../src/shell/shell_cmds.cpp"
47
48 namespace {
49
50 using namespace testing;
51
52 class DOS_Shell_CMDSTest : public DOSBoxTestFixture {};
53
54 class MockDOS_Shell : public DOS_Shell {
55 public:
56 /**
57 * NOTE: If we need to call the actual object, we use this. By
58 * default, the mocked functions return whatever we tell it to
59 * (if given a .WillOnce(Return(...)), or a default value
60 * (false).
61 *
62 * MockDOS_Shell()
63 * {
64 * // delegate call to the real object.
65 * ON_CALL(*this, execute_shell_cmd)
66 * .WillByDefault([this](char *name, char *arguments) {
67 * return real_.execute_shell_cmd(name, arguments);
68 * });
69 * }
70 */
71 MOCK_METHOD(bool, execute_shell_cmd, (char *name, char *arguments), (override));
72 MOCK_METHOD(void,
73 WriteOut,
74 (const char *format, const char *arguments),
75 (override));
76
77 private:
78 DOS_Shell real_; // Keeps an instance of the real in the mock.
79 };
80
assert_DoCommand(std::string input,std::string expected_name,std::string expected_args)81 void assert_DoCommand(std::string input, std::string expected_name, std::string expected_args)
82 {
83 MockDOS_Shell shell;
84 char *input_c_str = const_cast<char *>(input.c_str());
85 EXPECT_CALL(shell,
86 execute_shell_cmd(StrEq(expected_name), StrEq(expected_args)))
87 .Times(1)
88 .WillOnce(Return(true));
89 EXPECT_NO_THROW({ shell.DoCommand(input_c_str); });
90 }
91
92 // Tests chars that separate command name from arguments
TEST_F(DOS_Shell_CMDSTest,DoCommand_Separating_Chars)93 TEST_F(DOS_Shell_CMDSTest, DoCommand_Separating_Chars)
94 {
95 // These should all cause the parser to stop
96 std::vector<char> end_chars{
97 32,
98 '/',
99 '\t',
100 '=',
101 };
102 for (auto end_chr : end_chars) {
103 MockDOS_Shell shell;
104 std::string name = "PATH";
105 std::string args = "";
106 std::string input = name;
107 input += end_chr;
108 input += "ARG";
109 args += end_chr;
110 args += "ARG";
111 assert_DoCommand(input, name, args);
112 }
113 }
114
TEST_F(DOS_Shell_CMDSTest,DoCommand_All_Cmds_Do_Valid_Execute)115 TEST_F(DOS_Shell_CMDSTest, DoCommand_All_Cmds_Do_Valid_Execute)
116 {
117 MockDOS_Shell shell;
118 for (std::pair<std::string, SHELL_Cmd> cmd : shell_cmds) {
119 std::string input = cmd.first;
120 assert_DoCommand(input, input, "");
121 }
122 }
123
TEST_F(DOS_Shell_CMDSTest,DoCommand_Trim_Space)124 TEST_F(DOS_Shell_CMDSTest, DoCommand_Trim_Space)
125 {
126 assert_DoCommand(" PATH ", "PATH", "");
127 }
128
TEST_F(DOS_Shell_CMDSTest,DoCommand_Splits_Cmd_and_Args)129 TEST_F(DOS_Shell_CMDSTest, DoCommand_Splits_Cmd_and_Args)
130 {
131 // NOTE: It does not strip the arguments!
132 assert_DoCommand("DIR *.*", "DIR", " *.*");
133 }
134
TEST_F(DOS_Shell_CMDSTest,DoCommand_Doesnt_Split_Colon)135 TEST_F(DOS_Shell_CMDSTest, DoCommand_Doesnt_Split_Colon)
136 {
137 // ensure we don't split on colon ...
138 assert_DoCommand("C:", "C:", "");
139 // ... but it does split on slash
140 assert_DoCommand("C:\\", "C:", "\\");
141 }
142
TEST_F(DOS_Shell_CMDSTest,DoCommand_Nospace_Dot_Handling)143 TEST_F(DOS_Shell_CMDSTest, DoCommand_Nospace_Dot_Handling)
144 {
145 assert_DoCommand("DIR.EXE", "DIR", ".EXE");
146 assert_DoCommand("CD..", "CD", "..");
147 assert_DoCommand("CD....", "CD", "....");
148 }
149
TEST_F(DOS_Shell_CMDSTest,DoCommand_Nospace_Slash_Handling)150 TEST_F(DOS_Shell_CMDSTest, DoCommand_Nospace_Slash_Handling)
151 {
152 assert_DoCommand("CD\\DIRECTORY", "CD", "\\DIRECTORY");
153 assert_DoCommand("CD\\", "CD", "\\");
154 }
155
TEST_F(DOS_Shell_CMDSTest,CMD_ECHO_off_on)156 TEST_F(DOS_Shell_CMDSTest, CMD_ECHO_off_on)
157 {
158 MockDOS_Shell shell;
159 EXPECT_TRUE(shell.echo); // should be the default
160 EXPECT_CALL(shell, WriteOut(_, _)).Times(0);
161 EXPECT_NO_THROW({ shell.CMD_ECHO(const_cast<char *>("OFF")); });
162 EXPECT_FALSE(shell.echo);
163 EXPECT_NO_THROW({ shell.CMD_ECHO(const_cast<char *>("ON")); });
164 EXPECT_TRUE(shell.echo);
165 }
166
TEST_F(DOS_Shell_CMDSTest,CMD_ECHO_space_handling)167 TEST_F(DOS_Shell_CMDSTest, CMD_ECHO_space_handling)
168 {
169 MockDOS_Shell shell;
170
171 EXPECT_TRUE(shell.echo);
172 EXPECT_CALL(shell, WriteOut(_, StrEq("OFF "))).Times(1);
173 // this DOES NOT trigger ECHO OFF (trailing space causes it to not)
174 EXPECT_NO_THROW({ shell.CMD_ECHO(const_cast<char *>(" OFF ")); });
175 EXPECT_TRUE(shell.echo);
176
177 EXPECT_CALL(shell, WriteOut(_, StrEq("FF "))).Times(1);
178 // this DOES NOT trigger ECHO OFF (initial 'O' gets stripped)
179 EXPECT_NO_THROW({ shell.CMD_ECHO(const_cast<char *>("OFF ")); });
180 EXPECT_TRUE(shell.echo);
181
182 // no trailing space, echo off should work
183 EXPECT_CALL(shell, WriteOut(_, _)).Times(0);
184 EXPECT_NO_THROW({ shell.CMD_ECHO(const_cast<char *>(" OFF")); });
185 // check that OFF worked properly, despite spaces
186 EXPECT_FALSE(shell.echo);
187
188 // NOTE: the expected string here is missing the leading char of the
189 // input to ECHO. the first char is stripped as it's assumed it will be
190 // a space, period or slash.
191 EXPECT_CALL(shell, WriteOut(_, StrEq(" HI "))).Times(1);
192 EXPECT_NO_THROW({ shell.CMD_ECHO(const_cast<char *>(". HI ")); });
193 }
194
195 } // namespace
196