[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Patch] Provide some tests in gtest and shell forms for readline
From: |
wanghaitao (G) |
Subject: |
[Patch] Provide some tests in gtest and shell forms for readline |
Date: |
Fri, 14 Oct 2022 10:02:45 +0000 |
I don't see tests directory in readline, there is just some example files.
So I want to write some tests for readline.
I don't know how to write tests like calling it "make test" or "make check"
But I can write tests by gtest and shell.
Here is my tests patch. It add a tests subdirectory in readline.
tests
├── build_readline.sh
├── CMakeLists.txt
├── example_test_script write by shell
│ ├── history_dup.sh test examples/histexamp and
examples/hist_erasedups
│ ├── rlcallbacktest.sh test examples/rl-callbacktest
│ └── rlkeymaps.sh test examples/rlkeymaps
├── README.md
├── run_all_tests.sh
└── src write by gtest
├── history_test.cpp test add, modify, stifle, search, read
history
└─--readline_test.cpp test read a line, get previous
command, clear message
you can call the run_all_tests.sh to compile and run the tests.
By the way, I think my programming level is not so good and it is my first time
to upload a patch for GNU.
So If you think the code is not good or my spelling is wrong, I apologize for
that.
Wang Haitao
>From 9c6ec01889f471bce8bd18e96640ee09c596666e Mon Sep 17 00:00:00 2001
From: Wang Haitao <wanghaitao70@huawei.com>
Date: Fri, 14 Oct 2022 11:58:49 +0800
Subject: [PATCH] Add tests files in 2 forms: gtest and shell
Signed-off-by: Wang Haitao <wanghaitao70@huawei.com>
---
tests/CMakeLists.txt | 49 ++++++
tests/README.md | 32 ++++
tests/build_readline.sh | 20 +++
tests/example_test_script/history_dup.sh | 42 +++++
tests/example_test_script/rlcallbacktest.sh | 38 +++++
tests/example_test_script/rlkeymaps.sh | 6 +
tests/run_all_tests.sh | 20 +++
tests/src/history_test.cpp | 166 ++++++++++++++++++++
tests/src/readline_test.cpp | 119 ++++++++++++++
9 files changed, 492 insertions(+)
create mode 100644 tests/CMakeLists.txt
create mode 100644 tests/README.md
create mode 100755 tests/build_readline.sh create mode 100755
tests/example_test_script/history_dup.sh
create mode 100755 tests/example_test_script/rlcallbacktest.sh
create mode 100755 tests/example_test_script/rlkeymaps.sh
create mode 100755 tests/run_all_tests.sh create mode 100644
tests/src/history_test.cpp create mode 100644 tests/src/readline_test.cpp
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644
index 0000000..779f240
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,49 @@
+cmake_minimum_required(VERSION 3.14)
+project(readline_test)
+
+# GoogleTest requires at least C++14
+set(CMAKE_CXX_STANDARD 14)
+
+include(FetchContent)
+FetchContent_Declare(
+ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG release-1.12.1)
+# For Windows: Prevent overriding the parent project's compiler/linker
+settings set(gtest_force_shared_crt
+ ON
+ CACHE BOOL "" FORCE)
+FetchContent_MakeAvailable(googletest)
+
+enable_testing()
+
+aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src TEST_SRC)
+add_executable(readline_tests ${TEST_SRC})
+
+
+# use library in source directory instead of the library that OS
+provides set(READLINE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
+set(READLINE_SHLIB_DIR ${READLINE_DIR}/shlib)
+link_directories(${READLINE_SHLIB_DIR})
+
+# compile and install readline and history before build test project
+add_custom_target(
+ build_readline
+ COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_readline.sh
+ WORKING_DIRECTORY ${READLINE_DIR}
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/build_readline.sh
+ COMMENT "Start to configure and compile readline, please wait for few
minutes..."
+ VERBATIM)
+add_dependencies(readline_tests build_readline)
+
+# use headers in readline source files
+target_include_directories(readline_tests PRIVATE ${READLINE_DIR})
+
+# readline depend on tinfo
+set(READLINE_LIB ${READLINE_SHLIB_DIR}/libreadline.so
+${READLINE_SHLIB_DIR}/libhistory.so)
+target_link_libraries(readline_tests ${READLINE_LIB} tinfo)
+
+target_link_libraries(readline_tests GTest::gtest_main)
+
+include(GoogleTest)
+gtest_discover_tests(readline_tests)
diff --git a/tests/README.md b/tests/README.md new file mode 100644 index
0000000..3621b1d
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,32 @@
+# Description
+I don't see tests directory in readline, there is just some example files.
+So I want to write some tests for readline.
+
+I don't know how to write tests like calling it "make test" or "make check"
+But I can write by gtest and shell.
+
+Here is my tests directory.
+
+```text
+tests
+├── build_readline.sh
+├── CMakeLists.txt
+├── example_test_script write by shell
+│ ├── history_dup.sh test examples/histexamp and examples/hist_erasedups
+│ ├── rlcallbacktest.sh test examples/rl-callbacktest
+│ └── rlkeymaps.sh test examples/rlkeymaps
+├── README.md
+├── run_all_tests.sh
+└── src write by gtest
+ ├── history_test.cpp test add, modify, stifle, search, read history
+ └── readline_test.cpp test read a line, get previous command, clear
message
+```
+
+# Run tests
+build all gtest codes and run scrips then gtest:
+
+```sh
+./run_all_tests.sh
+```
+
+if shell scripts dones't return 1 and gtest report all tests pass, then we are
good.
\ No newline at end of file
diff --git a/tests/build_readline.sh b/tests/build_readline.sh new file mode
100755 index 0000000..d81cf4a
--- /dev/null
+++ b/tests/build_readline.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+# If haven't configure then configure
+if [ ! -f "Makefile" ]; then
+ ./configure
+
+ # install readline dependency for using library tinfo in linking.
+ if [ "$(command -v apt)" ]; then
+ sudo apt install -y libncurses5-dev
+ elif [ "$(command -v zypper)" ]; then
+ sudo zypper install -y ncurses-devel
+ fi
+fi
+
+make everything -j "$(nproc)"
+
+pushd shlib
+[[ ! -f libreadline.so ]] && ln -s libreadline.so.* libreadline.so [[ !
+-f libhistory.so ]] && ln -s libhistory.so.* libhistory.so popd
diff --git a/tests/example_test_script/history_dup.sh
b/tests/example_test_script/history_dup.sh
new file mode 100755
index 0000000..7a20a75
--- /dev/null
+++ b/tests/example_test_script/history_dup.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+dir=../../examples
+hist=$dir/histexamp
+dup=$dir/hist_erasedups
+
+cat <<EOT > input
+hello
+apple
+hello
+apple
+hello
+bigbang
+save
+quit
+EOT
+
+$hist < input > /dev/null
+
+# remove last line of input (quit)
+sed -i '$ d' input
+if ! diff input history_file; then
+ echo "failed! history_file diff from input!"
+ exit 1
+fi
+
+
+$dup -t history_file
+
+cat <<EOT > result
+apple
+hello
+bigbang
+save
+EOT
+
+if ! diff result history_file; then
+ echo "failed! history_file diff from result!"
+ exit 1
+fi
+
+rm input result history_file
\ No newline at end of file
diff --git a/tests/example_test_script/rlcallbacktest.sh
b/tests/example_test_script/rlcallbacktest.sh
new file mode 100755
index 0000000..dc7333c
--- /dev/null
+++ b/tests/example_test_script/rlcallbacktest.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+
+exe=../../examples/rl-callbacktest
+
+cat <<EOT > input
+hello
+apple
+banana
+lalala
+hahaha
+exit
+EOT
+
+cat <<EOT > result
+rltest$ hello
+input line: hello
+rltest$ apple
+input line: apple
+rltest$ banana
+input line: banana
+rltest$ lalala
+input line: lalala
+rltest$ hahaha
+input line: hahaha
+rltest$ exit
+exit
+rltest: Event loop has exited
+EOT
+
+$exe < input > output
+
+if ! diff output result; then
+ echo "failed! output diff from result!"
+ exit 1
+fi
+
+
+rm input output result
\ No newline at end of file
diff --git a/tests/example_test_script/rlkeymaps.sh
b/tests/example_test_script/rlkeymaps.sh
new file mode 100755
index 0000000..d41b37a
--- /dev/null
+++ b/tests/example_test_script/rlkeymaps.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+if ! ../../examples/rlkeymaps >/dev/null 2>&1; then
+ echo "failed!"
+ exit 1
+fi
diff --git a/tests/run_all_tests.sh b/tests/run_all_tests.sh new file mode
100755 index 0000000..6a92c6e
--- /dev/null
+++ b/tests/run_all_tests.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+if [[ ! -f build/readline_tests ]]; then
+ mkdir -p build && pushd build
+ cmake ..
+ make
+ popd
+fi
+
+pushd example_test_script || exit
+for script in ./*; do
+ if [ "$script" != "$0" ]; then
+ $script
+ fi
+done
+popd || exit
+
+pushd build
+./readline_tests
+popd
\ No newline at end of file
diff --git a/tests/src/history_test.cpp b/tests/src/history_test.cpp new file
mode 100644 index 0000000..a43fc81
--- /dev/null
+++ b/tests/src/history_test.cpp
@@ -0,0 +1,166 @@
+#include "gtest/gtest.h"
+#include "history.h"
+#include "xmalloc.h"
+
+#include <string>
+#include <fstream>
+
+class HistoryTest : public ::testing::Test {
+protected:
+ static const size_t kNum = 7;
+ const char *inputs[kNum] = {"apple", "banana", "candy", "ls", "cd /tmp",
"touch test", "echo hello > test"};
+ void SetUp() override
+ {
+ using_history();
+ clear_history();
+ unstifle_history();
+ }
+};
+
+HIST_ENTRY *get_latest_history()
+{
+ HIST_ENTRY *hist = history_list()[history_length - 1];
+ EXPECT_TRUE(hist != NULL);
+ return hist;
+}
+
+char *get_history_name(size_t index)
+{
+ auto history = history_list();
+ EXPECT_TRUE(history != NULL);
+ return history[index]->line;
+}
+
+TEST_F(HistoryTest, JustAddHistory)
+{
+ for (size_t i = 0; i < kNum; i++)
+ {
+ add_history(inputs[i]);
+ ASSERT_STREQ(get_history_name(i), inputs[i]);
+ ASSERT_EQ(history_length, i + 1);
+ }
+}
+
+TEST_F(HistoryTest, StifiledHistory)
+{
+ add_history("apple");
+ add_history("banana");
+ add_history("candy");
+ ASSERT_EQ(history_length, 3);
+
+ stifle_history(2);
+ ASSERT_EQ(history_is_stifled(), 1);
+
+ ASSERT_STREQ(get_history_name(0), "banana");
+ ASSERT_STREQ(get_history_name(1), "candy");
+ ASSERT_EQ(history_length, 2);
+
+ add_history("edge");
+ ASSERT_STREQ(get_history_name(0), "candy");
+ ASSERT_STREQ(get_history_name(1), "edge"); }
+
+TEST_F(HistoryTest, SearchHistory)
+{
+ for (size_t i = 0; i < kNum; i++)
+ {
+ add_history(inputs[i]);
+ }
+
+ int pos = history_search_pos("cd", 1, 0);
+ ASSERT_EQ(pos, 4);
+ ASSERT_STREQ(get_history_name(pos), inputs[4]);
+
+ pos = history_search_pos("ba", -1, pos);
+ ASSERT_EQ(pos, 1);
+ ASSERT_STREQ(get_history_name(pos), inputs[1]); }
+
+void CheckHistory(const char *filename, std::string file_should_be) {
+ std::ifstream ifs(filename);
+ std::string read_file((std::istreambuf_iterator<char>(ifs)),
+ std::istreambuf_iterator<char>());
+ ASSERT_EQ(read_file, file_should_be); }
+
+TEST_F(HistoryTest, ReadHistory)
+{
+ int ret;
+ const char *filename = ".history";
+
+ for (size_t i = 0; i < kNum; i++)
+ {
+ add_history(inputs[i]);
+ }
+
+ ret = write_history(filename);
+ ASSERT_EQ(ret, 0) << strerror(errno);
+
+ clear_history();
+ ASSERT_EQ(history_list()[0], nullptr);
+ ASSERT_EQ(history_length, 0);
+
+ ret = read_history(filename);
+ ASSERT_EQ(ret, 0) << strerror(errno);
+
+ for (size_t i = 0; i < kNum; i++)
+ {
+ ASSERT_STREQ(get_history_name(i), inputs[i]);
+ }
+}
+
+TEST_F(HistoryTest, ModifyHistoryFile)
+{
+ int ret;
+ const char *filename = ".history";
+ const size_t len = 4;
+ std::string file_should_be;
+
+ for (size_t i = 0; i < len; i++)
+ {
+ add_history(inputs[i]);
+ file_should_be.append(inputs[i]);
+ file_should_be.append("\n");
+ }
+
+ ret = write_history(filename);
+ ASSERT_EQ(ret, 0) << strerror(errno);
+ CheckHistory(filename, file_should_be.c_str());
+
+ for (size_t i = len; i < kNum; i++)
+ {
+ add_history(inputs[i]);
+ file_should_be.append(inputs[i]);
+ file_should_be.append("\n");
+ }
+
+ ret = append_history(kNum - len, filename);
+ ASSERT_EQ(ret, 0) << strerror(errno);
+ CheckHistory(filename, file_should_be.c_str());
+
+ ret = history_truncate_file(filename, 0);
+ ASSERT_EQ(ret, 0) << strerror(errno);
+ std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
+ ASSERT_EQ(in.tellg(), 0);
+
+ remove(filename);
+}
+
+TEST_F(HistoryTest, TruncateError)
+{
+ int ret;
+ const char *test_dir = ".test_dir";
+
+ ret =
history_truncate_file("/This/is/a/file/that/does/not/exist/123123#%$#)((DSHF",
0);
+ ASSERT_NE(ret, 0);
+ ASSERT_EQ(errno, ENOENT);
+
+ mkdir(test_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ ret = history_truncate_file(test_dir, 0);
+ ASSERT_NE(ret, 0);
+
+ ret = rmdir(test_dir);
+ ASSERT_EQ(ret, 0);
+}
\ No newline at end of file
diff --git a/tests/src/readline_test.cpp b/tests/src/readline_test.cpp new file
mode 100644 index 0000000..146bb4f
--- /dev/null
+++ b/tests/src/readline_test.cpp
@@ -0,0 +1,119 @@
+#include <cstdio>
+#include <cerrno>
+#include <cstdlib>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "history.h"
+#include "readline.h"
+
+using std::string;
+using testing::internal::CaptureStdout; using
+testing::internal::GetCapturedStdout;
+
+class ReadlineTest : public ::testing::Test {
+protected:
+ const char *prompt = "Handsome haitao$ ";
+ int pip[2];
+ static const size_t kNum = 7;
+ const char *inputs[kNum] = {"apple", "banana", "candy", "ls", "cd
+/tmp", "touch test", "echo hello > test"};
+
+ void SetUp() override
+ {
+ // clean readline history and anything else
+ rl_clear_history();
+ rl_clear_signals();
+
+ // Let readline read from pipe intead of stdin
+ ASSERT_EQ(pipe(pip), 0);
+ ASSERT_NE(dup2(pip[0], STDIN_FILENO), -1);
+ }
+
+ // Write to pipe to simulate terminal write
+ void MockTerminalWrite(const char *line)
+ {
+ ssize_t len = write(pip[1], line, strlen(line));
+ ASSERT_NE(len, -1) << strerror(errno);
+ }
+
+ void MockTerminalWrite(char c)
+ {
+ ssize_t len = write(pip[1], &c, sizeof(char));
+ ASSERT_NE(len, -1) << strerror(errno);
+ }
+};
+
+TEST_F(ReadlineTest, JustReadALine)
+{
+ char *line;
+ string output;
+
+ for (size_t i = 0; i < kNum; i++)
+ {
+ CaptureStdout();
+ MockTerminalWrite(inputs[i]);
+ MockTerminalWrite('\n');
+ line = readline(prompt);
+ output = GetCapturedStdout();
+ ASSERT_STREQ(line, inputs[i]);
+ ASSERT_EQ(output, string(prompt) + inputs[i] + "\n");
+ free(line);
+ }
+}
+
+TEST_F(ReadlineTest, PreviousCommands)
+{
+ char *line;
+ std::string output;
+ for (int i = 0; i < kNum; i++)
+ {
+ MockTerminalWrite(inputs[i]);
+ MockTerminalWrite('\n');
+ line = readline(prompt);
+ add_history(line);
+ free(line);
+ }
+
+ for (int i = kNum - 2; i >= 0; i--)
+ {
+ rl_get_previous_history(1, 0);
+ ASSERT_STREQ(rl_line_buffer, inputs[i]);
+ }
+
+ for (int i = 1; i < kNum; i++)
+ {
+ rl_get_previous_history(-1, 0);
+ ASSERT_STREQ(rl_line_buffer, inputs[i]);
+ }
+}
+
+int clear_message(int __, int _)
+{
+ return rl_clear_message();
+}
+
+
+TEST_F(ReadlineTest, JustClearMessage)
+{
+ const char *input = "ls";
+ char * line;
+ string output;
+
+ rl_add_defun("clear_message", clear_message, CTRL('t'));
+
+ CaptureStdout();
+ MockTerminalWrite(input);
+ MockTerminalWrite('\n');
+ line = readline(prompt);
+ output = GetCapturedStdout();
+ ASSERT_EQ(output, string(prompt) + input + "\n");
+ free(line);
+
+ // CaptureStdout();
+ MockTerminalWrite(input);
+ MockTerminalWrite(CTRL('t'));
+ MockTerminalWrite('\n');
+ line = readline(prompt);
+ free(line);
+}
\ No newline at end of file
--
2.17.1
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Patch] Provide some tests in gtest and shell forms for readline,
wanghaitao (G) <=