add shell unit test
This commit is contained in:
parent
4f26e2519f
commit
7073e4d082
|
@ -2,13 +2,18 @@ cmake_minimum_required(VERSION 3.9)
|
||||||
|
|
||||||
project("libopenfpgashell")
|
project("libopenfpgashell")
|
||||||
|
|
||||||
file(GLOB_RECURSE EXEC_SOURCES test/test_command_parser.cpp)
|
# We need readline to compile
|
||||||
|
find_package(Readline REQUIRED)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE EXEC_TEST_SHELL test/test_shell.cpp)
|
||||||
|
file(GLOB_RECURSE EXEC_TEST_CMD test/test_command_parser.cpp)
|
||||||
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
|
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
|
||||||
file(GLOB_RECURSE LIB_HEADERS src/*.h)
|
file(GLOB_RECURSE LIB_HEADERS src/*.h)
|
||||||
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
||||||
|
|
||||||
#Remove test executable from library
|
#Remove test executable from library
|
||||||
list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES})
|
list(REMOVE_ITEM LIB_SOURCES ${EXEC_TEST_SHELL})
|
||||||
|
list(REMOVE_ITEM LIB_SOURCES ${EXEC_TEST_CMD})
|
||||||
|
|
||||||
#Create the library
|
#Create the library
|
||||||
add_library(libopenfpgashell STATIC
|
add_library(libopenfpgashell STATIC
|
||||||
|
@ -20,10 +25,14 @@ set_target_properties(libopenfpgashell PROPERTIES PREFIX "") #Avoid extra 'lib'
|
||||||
#Specify link-time dependancies
|
#Specify link-time dependancies
|
||||||
target_link_libraries(libopenfpgashell
|
target_link_libraries(libopenfpgashell
|
||||||
libopenfpgautil
|
libopenfpgautil
|
||||||
libvtrutil)
|
libvtrutil
|
||||||
|
readline)
|
||||||
|
|
||||||
#Create the test executable
|
#Create the test executable
|
||||||
add_executable(test_command_parser ${EXEC_SOURCES})
|
add_executable(test_shell ${EXEC_TEST_SHELL})
|
||||||
|
target_link_libraries(test_shell libopenfpgashell)
|
||||||
|
|
||||||
|
add_executable(test_command_parser ${EXEC_TEST_CMD})
|
||||||
target_link_libraries(test_command_parser libopenfpgashell)
|
target_link_libraries(test_command_parser libopenfpgashell)
|
||||||
|
|
||||||
#Supress IPO link warnings if IPO is enabled
|
#Supress IPO link warnings if IPO is enabled
|
||||||
|
|
|
@ -68,11 +68,11 @@ class Shell {
|
||||||
const CommandContext& command_context(const ShellCommandId& cmd_id) const;
|
const CommandContext& command_context(const ShellCommandId& cmd_id) const;
|
||||||
std::vector<ShellCommandId> command_dependency(const ShellCommandId& cmd_id) const;
|
std::vector<ShellCommandId> command_dependency(const ShellCommandId& cmd_id) const;
|
||||||
public: /* Public mutators */
|
public: /* Public mutators */
|
||||||
void set_title(const char* title);
|
void add_title(const char* title);
|
||||||
ShellCommandId add_command(const Command& cmd, const char* descr);
|
ShellCommandId add_command(const Command& cmd, const char* descr);
|
||||||
void add_command_execute_function(const ShellCommandId& cmd_id,
|
void set_command_execute_function(const ShellCommandId& cmd_id,
|
||||||
std::function<void(T, const CommandContext&)> exec_func);
|
std::function<void(T&, const Command&, const CommandContext&)> exec_func);
|
||||||
void add_command_dependency(const ShellCommandId& cmd_id,
|
void set_command_dependency(const ShellCommandId& cmd_id,
|
||||||
const std::vector<ShellCommandId> cmd_dependency);
|
const std::vector<ShellCommandId> cmd_dependency);
|
||||||
public: /* Public validators */
|
public: /* Public validators */
|
||||||
bool valid_command_id(const ShellCommandId& cmd_id) const;
|
bool valid_command_id(const ShellCommandId& cmd_id) const;
|
||||||
|
@ -106,7 +106,7 @@ class Shell {
|
||||||
vtr::vector<ShellCommandId, std::string> command_description_;
|
vtr::vector<ShellCommandId, std::string> command_description_;
|
||||||
|
|
||||||
/* Function pointers to execute each command */
|
/* Function pointers to execute each command */
|
||||||
vtr::vector<ShellCommandId, std::function<void(T, const CommandContext&)>> command_execute_functions_;
|
vtr::vector<ShellCommandId, std::function<void(T&, const Command&, const CommandContext&)>> command_execute_functions_;
|
||||||
|
|
||||||
/* Dependency graph for different commands,
|
/* Dependency graph for different commands,
|
||||||
* This helps the shell interface to check if a command need other commands to be run before its execution
|
* This helps the shell interface to check if a command need other commands to be run before its execution
|
||||||
|
@ -119,4 +119,7 @@ class Shell {
|
||||||
|
|
||||||
} /* End namespace minshell */
|
} /* End namespace minshell */
|
||||||
|
|
||||||
|
/* Include the implementation functions in the header file */
|
||||||
|
#include "shell.tpp"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
/* Headers from openfpgashell library */
|
/* Headers from openfpgashell library */
|
||||||
#include "command_parser.h"
|
#include "command_parser.h"
|
||||||
#include "command_echo.h"
|
#include "command_echo.h"
|
||||||
#include "shell.h"
|
|
||||||
|
|
||||||
/* Begin namespace minishell */
|
/* Begin namespace minishell */
|
||||||
namespace minishell {
|
namespace minishell {
|
||||||
|
@ -86,7 +85,7 @@ std::vector<ShellCommandId> Shell<T>::command_dependency(const ShellCommandId& c
|
||||||
* Public mutators
|
* Public mutators
|
||||||
***********************************************************************/
|
***********************************************************************/
|
||||||
template<class T>
|
template<class T>
|
||||||
void Shell<T>::set_title(const char* title) {
|
void Shell<T>::add_title(const char* title) {
|
||||||
title_ = std::string(title);
|
title_ = std::string(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,20 +108,20 @@ ShellCommandId Shell<T>::add_command(const Command& cmd, const char* descr) {
|
||||||
command_dependencies_.emplace_back();
|
command_dependencies_.emplace_back();
|
||||||
|
|
||||||
/* Register the name in the name2id map */
|
/* Register the name in the name2id map */
|
||||||
command_name2ids_[std::string(name)] = cmd.name();
|
command_name2ids_[cmd.name()] = shell_cmd;
|
||||||
|
|
||||||
return shell_cmd;
|
return shell_cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void Shell<T>::add_command_execute_function(const ShellCommandId& cmd_id,
|
void Shell<T>::set_command_execute_function(const ShellCommandId& cmd_id,
|
||||||
std::function<void(T, const CommandContext&)> exec_func) {
|
std::function<void(T&, const Command&, const CommandContext&)> exec_func) {
|
||||||
VTR_ASSERT(true == valid_command_id(cmd_id));
|
VTR_ASSERT(true == valid_command_id(cmd_id));
|
||||||
command_execute_functions_[cmd_id] = exec_func;
|
command_execute_functions_[cmd_id] = exec_func;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void Shell<T>::add_command_dependency(const ShellCommandId& cmd_id,
|
void Shell<T>::set_command_dependency(const ShellCommandId& cmd_id,
|
||||||
const std::vector<ShellCommandId> dependent_cmds) {
|
const std::vector<ShellCommandId> dependent_cmds) {
|
||||||
/* Validate the command id as well as each of the command dependency */
|
/* Validate the command id as well as each of the command dependency */
|
||||||
VTR_ASSERT(true == valid_command_id(cmd_id));
|
VTR_ASSERT(true == valid_command_id(cmd_id));
|
||||||
|
@ -142,7 +141,7 @@ void Shell<T>::run_interactive_mode(T& context) {
|
||||||
|
|
||||||
/* Print the title of the shell */
|
/* Print the title of the shell */
|
||||||
if (!title().empty()) {
|
if (!title().empty()) {
|
||||||
VTR_LOG("%s\n", title());
|
VTR_LOG("%s\n", title().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for users input and execute the command */
|
/* Wait for users input and execute the command */
|
||||||
|
@ -169,7 +168,7 @@ void Shell<T>::run_script_mode(const char* script_file_name, T& context) {
|
||||||
|
|
||||||
/* Print the title of the shell */
|
/* Print the title of the shell */
|
||||||
if (!title().empty()) {
|
if (!title().empty()) {
|
||||||
VTR_LOG("%s\n", title());
|
VTR_LOG("%s\n", title().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
|
@ -232,7 +231,7 @@ void Shell<T>::execute_command(const char* cmd_line,
|
||||||
print_command_context(commands_[cmd_id], command_contexts_[cmd_id]);
|
print_command_context(commands_[cmd_id], command_contexts_[cmd_id]);
|
||||||
|
|
||||||
/* Execute the command! */
|
/* Execute the command! */
|
||||||
command_execute_functions_[cmd_id](common_context, command_contexts_[cmd_id]);
|
command_execute_functions_[cmd_id](common_context, commands_[cmd_id], command_contexts_[cmd_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************
|
/************************************************************************
|
|
@ -0,0 +1,94 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Test the shell interface by pre-defining simple commands
|
||||||
|
* like exit() and help()
|
||||||
|
*******************************************************************/
|
||||||
|
#include "vtr_log.h"
|
||||||
|
#include "command_parser.h"
|
||||||
|
#include "command_echo.h"
|
||||||
|
#include "shell.h"
|
||||||
|
|
||||||
|
using namespace minishell;
|
||||||
|
|
||||||
|
class ShellContext {
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
void shell_cmd_help_executor(ShellContext& context,
|
||||||
|
const Command& cmd,
|
||||||
|
const CommandContext& cmd_context) {
|
||||||
|
VTR_LOG("Help desk:\n");
|
||||||
|
VTR_LOG("Available commands:\n");
|
||||||
|
VTR_LOG("help\texit\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void shell_cmd_exit_executor(ShellContext& context,
|
||||||
|
const Command& cmd,
|
||||||
|
const CommandContext& cmd_context) {
|
||||||
|
VTR_LOG("Thank you for using!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
/* Create the command to launch shell in different modes */
|
||||||
|
Command start_cmd("test_shell");
|
||||||
|
/* Add two options:
|
||||||
|
* '--interactive', -i': launch the interactive mode
|
||||||
|
* '--file', -f': launch the script mode
|
||||||
|
*/
|
||||||
|
CommandOptionId opt_interactive = start_cmd.add_option("interactive", false, "Launch the shell in interactive mode");
|
||||||
|
start_cmd.set_option_short_name(opt_interactive, "i");
|
||||||
|
|
||||||
|
CommandOptionId opt_script_mode = start_cmd.add_option("file", false, "Launch the shell in script mode");
|
||||||
|
start_cmd.set_option_require_value(opt_script_mode, OPT_STRING);
|
||||||
|
start_cmd.set_option_short_name(opt_script_mode, "f");
|
||||||
|
|
||||||
|
CommandOptionId opt_help = start_cmd.add_option("help", false, "Help desk");
|
||||||
|
start_cmd.set_option_short_name(opt_help, "h");
|
||||||
|
|
||||||
|
/* Create a shell object
|
||||||
|
* Add two commands, which are
|
||||||
|
* 1. help
|
||||||
|
* 2. exit
|
||||||
|
*/
|
||||||
|
Shell<ShellContext> shell("test_shell");
|
||||||
|
shell.add_title("This is a simple test shell\nAuthor: Xifan Tang\n");
|
||||||
|
|
||||||
|
Command shell_cmd_help("help");
|
||||||
|
ShellCommandId shell_cmd_help_id = shell.add_command(shell_cmd_help, "Launch help desk");
|
||||||
|
shell.set_command_execute_function(shell_cmd_help_id, shell_cmd_help_executor);
|
||||||
|
|
||||||
|
Command shell_cmd_exit("exit");
|
||||||
|
ShellCommandId shell_cmd_exit_id = shell.add_command(shell_cmd_exit, "Exit the shell");
|
||||||
|
shell.set_command_execute_function(shell_cmd_exit_id, shell_cmd_exit_executor);
|
||||||
|
|
||||||
|
/* Create the data base for the shell */
|
||||||
|
ShellContext shell_context;
|
||||||
|
|
||||||
|
/* Parse the option, to avoid issues, we use the command name to replace the argv[0] */
|
||||||
|
std::vector<std::string> cmd_opts;
|
||||||
|
cmd_opts.push_back(start_cmd.name());
|
||||||
|
for (int iarg = 1; iarg < argc; ++iarg) {
|
||||||
|
cmd_opts.push_back(std::string(argv[iarg]));
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandContext start_cmd_context(start_cmd);
|
||||||
|
if (false == parse_command(cmd_opts, start_cmd, start_cmd_context)) {
|
||||||
|
/* Parse fail: Echo the command */
|
||||||
|
print_command_options(start_cmd);
|
||||||
|
} else {
|
||||||
|
/* Parse succeed. Start a shell */
|
||||||
|
if (true == start_cmd_context.option_enable(start_cmd, opt_interactive)) {
|
||||||
|
shell.run_interactive_mode(shell_context);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true == start_cmd_context.option_enable(start_cmd, opt_script_mode)) {
|
||||||
|
shell.run_script_mode(start_cmd_context.option_value(start_cmd, opt_script_mode).c_str(),
|
||||||
|
shell_context);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue