kyaa/test.c

411 lines
11 KiB
C

#include <assert.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "greatest.h"
GREATEST_MAIN_DEFS();
// use non-default return codes to test that they actually change.
// this also allows distinguishing KYAA_OKAY from the user's main() returning 0.
#define KYAA_OKAY 123
#define KYAA_FAIL -666
// capture stdout and stderr for later testing.
#define CAPTURE_SIZE 2048
static char global_buf[CAPTURE_SIZE] = {0};
static char global_out[CAPTURE_SIZE] = {0};
static char global_err[CAPTURE_SIZE] = {0};
static int out_offset = 0;
static int err_offset = 0;
static bool out_overflown = false;
static bool err_overflown = false;
static int capture_stdout(int printf_result) {
if (printf_result < 0 || printf_result >= CAPTURE_SIZE) goto fail;
for (int i = 0; i < printf_result; i++) {
// copy as much as possible before capping out.
if (out_offset >= CAPTURE_SIZE) goto fail;
global_out[out_offset] = global_buf[i];
out_offset++;
}
return printf_result;
fail:
out_overflown = true;
return printf_result;
}
static int capture_stderr(int printf_result) {
if (printf_result < 0 || printf_result >= CAPTURE_SIZE) goto fail;
for (int i = 0; i < printf_result; i++) {
// copy as much as possible before capping out.
if (err_offset >= CAPTURE_SIZE) goto fail;
global_err[err_offset] = global_buf[i];
err_offset++;
}
return printf_result;
fail:
err_overflown = true;
return printf_result;
}
#define KYAA_OUT(...) capture_stdout(snprintf(global_buf, CAPTURE_SIZE, __VA_ARGS__))
#define KYAA_ERR(...) capture_stderr(snprintf(global_buf, CAPTURE_SIZE, __VA_ARGS__))
#include "kyaa.h"
#include "kyaa_extra.h"
#define main(a, b) subject(a, const b)
#include "test_subject.c"
#undef main
// testing begins here!
#define ASSERT_ZERO(ret) ASSERT_EQ(ret, 0)
#define ASSERT_OKAY(ret) ASSERT_EQ(ret, KYAA_OKAY)
#define ASSERT_FAIL(ret) ASSERT_EQ(ret, KYAA_FAIL)
#define ASSERT_OUT(str) do { \
ASSERT_FALSE(out_overflown); \
ASSERT_STR_EQ(str, global_out); \
} while (0)
#define ASSERT_ERR(str) do { \
ASSERT_FALSE(err_overflown); \
ASSERT_STR_EQ(str, global_err); \
} while (0)
static void reset() {
use_feature = false;
log_fn = "log.txt";
my_var = 0;
read_stdin = false;
out_offset = 0;
err_offset = 0;
memset(global_buf, 0, CAPTURE_SIZE);
memset(global_out, 0, CAPTURE_SIZE);
memset(global_err, 0, CAPTURE_SIZE);
return;
}
int kyaa(int argc, const char **argv) {
reset();
return subject(argc, argv);
}
int kyaa_va(int argc, ...) {
static const char *argv[256] = {0};
va_list args;
assert(argc <= 256);
va_start(args, argc);
for (int i = 0; i < argc; i++) {
argv[i] = va_arg(args, const char *);
}
va_end(args);
return kyaa(argc, argv);
}
static const char *blank = "";
static const char *basic_argv[] = {"kyaa"};
static const char *help_text = "usage:\n\
-x --enable-feature\n\
enable some feature\n\
-l --log-file\n\
use a given filename for the log file\n\
-v --var\n\
set an integer variable\n\
default: 0\n\
";
#define ASSERT_VALUES_UNCHANGED() do { \
ASSERT(!use_feature); \
ASSERT_STR_EQ("log.txt", log_fn); \
ASSERT_EQ(0, my_var); \
} while (0)
#define ASSERT_VALUES_CHANGED() do { \
ASSERT(use_feature); \
ASSERT_STR_EQ("/var/log/kyaa", log_fn); \
ASSERT_EQ(1337, my_var); \
} while (0)
TEST no_arguments() { // (as if invoked by a shell)
ASSERT_ZERO(kyaa(1, basic_argv));
ASSERT_VALUES_UNCHANGED();
PASS();
}
TEST one_flag() {
ASSERT_ZERO(kyaa_va(2, "kyaa", "-x"));
ASSERT(use_feature);
PASS();
}
TEST arg_flag() {
ASSERT_ZERO(kyaa_va(3, "kyaa", "-l", "/var/log/kyaa"));
ASSERT_EQ("/var/log/kyaa", log_fn);
ASSERT_ZERO(kyaa_va(3, "kyaa", "-v", "1337"));
ASSERT_EQ(1337, my_var);
PASS();
}
TEST undefined_flag() {
ASSERT_FAIL(kyaa_va(2, "kyaa", "-p"));
ASSERT_ERR("unknown flag: -p\n");
ASSERT_FAIL(kyaa_va(3, "kyaa", "-o", "outfile"));
ASSERT_ERR("unknown flag: -o\n");
PASS();
}
TEST many_flags() {
ASSERT_ZERO(kyaa_va(6, "kyaa", "-v", "1337", "-l", "/var/log/kyaa", "-x")); ASSERT_VALUES_CHANGED();
ASSERT_ZERO(kyaa_va(6, "kyaa", "-v", "1337", "-x", "-l", "/var/log/kyaa")); ASSERT_VALUES_CHANGED();
ASSERT_ZERO(kyaa_va(6, "kyaa", "-l", "/var/log/kyaa", "-v", "1337", "-x")); ASSERT_VALUES_CHANGED();
ASSERT_ZERO(kyaa_va(6, "kyaa", "-l", "/var/log/kyaa", "-x", "-v", "1337")); ASSERT_VALUES_CHANGED();
ASSERT_ZERO(kyaa_va(6, "kyaa", "-x", "-v", "1337", "-l", "/var/log/kyaa")); ASSERT_VALUES_CHANGED();
ASSERT_ZERO(kyaa_va(6, "kyaa", "-x", "-l", "/var/log/kyaa", "-v", "1337")); ASSERT_VALUES_CHANGED();
PASS();
}
TEST empty_flag() {
ASSERT_ZERO(kyaa_va(3, "kyaa", "-l", ""));
ASSERT_STR_EQ("", log_fn);
PASS();
}
TEST empty_long_flag() {
ASSERT_ZERO(kyaa_va(3, "kyaa", "--log-file", ""));
ASSERT_STR_EQ("", log_fn);
PASS();
}
TEST empty_long_flag_int() {
ASSERT_FAIL(kyaa_va(3, "kyaa", "--var", ""));
PASS();
}
TEST long_flag() {
ASSERT_ZERO(kyaa_va(2, "kyaa", "--enable-feature"));
PASS();
}
TEST long_flag_arg() {
ASSERT_ZERO(kyaa_va(3, "kyaa", "--log-file", "/var/log/kyaa"));
ASSERT_ZERO(kyaa_va(3, "kyaa", "--var", "1337"));
PASS();
}
TEST long_flag_equals() {
ASSERT_ZERO(kyaa_va(2, "kyaa", "--log-file=/var/log/kyaa"));
ASSERT_ZERO(kyaa_va(2, "kyaa", "--var=1337"));
PASS();
}
TEST long_flag_equals_empty() {
ASSERT_ZERO(kyaa_va(2, "kyaa", "--log-file="));
PASS();
}
TEST long_flag_help_arg() {
ASSERT_ZERO(kyaa_va(3, "kyaa", "-l", "-h"));
ASSERT_STR_EQ("-h", log_fn);
ASSERT_OUT(blank);
ASSERT_ZERO(kyaa_va(3, "kyaa", "--log-file", "--help"));
ASSERT_STR_EQ("--help", log_fn);
ASSERT_OUT(blank);
PASS();
}
TEST long_flag_equals_empty_int() {
ASSERT_FAIL(kyaa_va(2, "kyaa", "--var="));
PASS();
}
TEST undefined_long_flag() {
ASSERT_FAIL(kyaa_va(2, "kyaa", "--print"));
ASSERT_ERR("unknown flag: --print\n");
ASSERT_FAIL(kyaa_va(3, "kyaa", "--out", "outfile"));
ASSERT_ERR("unknown flag: --out\n");
ASSERT_FAIL(kyaa_va(3, "kyaa", "--log", "/var/log/kyaa"));
ASSERT_ERR("unknown flag: --log\n");
ASSERT_FAIL(kyaa_va(2, "kyaa", "--log=/var/log/kyaa"));
ASSERT_ERR("unknown flag: --log=/var/log/kyaa\n");
PASS();
}
TEST dangling_arg() {
ASSERT_FAIL(kyaa_va(2, "kyaa", "-l"));
ASSERT_OUT(blank);
ASSERT_ERR("expected an argument for --log-file (-l)\n");
ASSERT_FAIL(kyaa_va(2, "kyaa", "-v"));
ASSERT_OUT(blank);
ASSERT_ERR("expected an argument for --var (-v)\n");
PASS();
}
TEST valid_int() {
ASSERT_ZERO(kyaa_va(3, "kyaa", "-v", "123456789"));
ASSERT_ZERO(kyaa_va(3, "kyaa", "-v", "-1"));
ASSERT_ZERO(kyaa_va(3, "kyaa", "-v", "0"));
ASSERT_ZERO(kyaa_va(3, "kyaa", "-v", "1"));
ASSERT_ZERO(kyaa_va(3, "kyaa", "-v", "-2147483648"));
ASSERT_EQ(-2147483648, my_var);
ASSERT_ZERO(kyaa_va(3, "kyaa", "-v", "2147483647"));
ASSERT_ZERO(kyaa_va(3, "kyaa", "-v", "0xBADF00D"));
ASSERT_EQ(0xBADF00D, my_var);
ASSERT_ZERO(kyaa_va(3, "kyaa", "-v", "0755"));
ASSERT_EQ(0755, my_var);
PASS();
}
TEST invalid_int() {
ASSERT_FAIL(kyaa_va(3, "kyaa", "-v", "hey"));
ASSERT_FAIL(kyaa_va(3, "kyaa", "-v", " "));
ASSERT_FAIL(kyaa_va(3, "kyaa", "-v", "0xG00DF00D"));
ASSERT_FAIL(kyaa_va(3, "kyaa", "-v", "123abc"));
ASSERT_FAIL(kyaa_va(3, "kyaa", "-v", "0123456789"));
ASSERT_FAIL(kyaa_va(3, "kyaa", "-v", "--"));
PASS();
}
TEST huge_int() {
ASSERT_FAIL(kyaa_va(3, "kyaa", "-v", "2147483648"));
ASSERT_FAIL(kyaa_va(3, "kyaa", "-v", "-2147483649"));
PASS();
}
TEST help() {
static const char *argv1[] = {"kyaa", "-h"};
static const char *argv2[] = {"kyaa", "--help"};
/* static const char *argv3[] = {"kyaa", "-?"}; */
/* static const char *argv4[] = {"kyaa", "/?"}; */
ASSERT_OKAY(kyaa(2, argv1));
ASSERT_OUT(help_text);
ASSERT_ERR(blank);
ASSERT_OKAY(kyaa(2, argv2));
ASSERT_OUT(help_text);
ASSERT_ERR(blank);
PASS();
}
TEST use_stdin() {
ASSERT_ZERO(kyaa_va(2, "kyaa", "-"));
ASSERT(read_stdin);
ASSERT_OUT("-\n");
PASS();
}
TEST stop_parsing() {
ASSERT_ZERO(kyaa_va(2, "kyaa", "--"));
ASSERT_FALSE(read_stdin);
ASSERT_ZERO(kyaa_va(5, "kyaa", "--", "blah", "--blah", "-b"));
ASSERT_OUT("blah\n--blah\n-b\n");
ASSERT_ZERO(kyaa_va(3, "kyaa", "--", "-x"));
ASSERT_VALUES_UNCHANGED();
ASSERT_ZERO(kyaa_va(4, "kyaa", "--", "-h", "--help"));
ASSERT_OUT("-h\n--help\n");
ASSERT_ERR(blank);
PASS();
}
TEST keep_parsing() {
ASSERT_ZERO(kyaa_va(4, "kyaa", "-l", "--", "-x"));
ASSERT_STR_EQ("--", log_fn);
ASSERT(use_feature);
PASS();
}
TEST dangling_arg_oob() {
// intentionally passing an inaccurate argc here:
ASSERT_FAIL(kyaa_va(2, "kyaa", "-l", "don't read this"));
ASSERT_OUT(blank);
ASSERT_ERR("expected an argument for --log-file (-l)\n");
ASSERT_FAIL(kyaa_va(2, "kyaa", "-v", "don't read this"));
ASSERT_OUT(blank);
ASSERT_ERR("expected an argument for --var (-v)\n");
PASS();
}
TEST arg_oob() {
ASSERT_FAIL(kyaa_va(3, "kyaa", "-l", NULL));
ASSERT_OUT(blank);
ASSERT_ERR("expected an argument for --log-file (-l)\n");
ASSERT_FAIL(kyaa_va(3, "kyaa", "-v", NULL));
ASSERT_OUT(blank);
ASSERT_ERR("expected an argument for --var (-v)\n");
PASS();
}
TEST negative_argc() {
ASSERT_FAIL(kyaa(-1, basic_argv));
ASSERT_FAIL(kyaa(LONG_MIN, basic_argv));
PASS();
}
TEST null_argv() {
ASSERT_FAIL(kyaa(0, NULL));
ASSERT_FAIL(kyaa(1, NULL));
ASSERT_FAIL(kyaa(2, NULL));
ASSERT_FAIL(kyaa(-1, NULL));
ASSERT_FAIL(kyaa(LONG_MIN, NULL));
PASS();
}
SUITE(goods) {
RUN_TEST(no_arguments);
RUN_TEST(one_flag);
RUN_TEST(arg_flag);
RUN_TEST(many_flags);
RUN_TEST(empty_flag);
RUN_TEST(empty_long_flag);
RUN_TEST(long_flag);
RUN_TEST(long_flag_arg);
RUN_TEST(long_flag_equals);
RUN_TEST(long_flag_equals_empty);
RUN_TEST(long_flag_help_arg);
RUN_TEST(valid_int);
RUN_TEST(help);
RUN_TEST(use_stdin);
RUN_TEST(stop_parsing);
RUN_TEST(keep_parsing);
}
SUITE(bads) {
RUN_TEST(undefined_flag);
RUN_TEST(empty_long_flag_int);
RUN_TEST(long_flag_equals_empty_int);
RUN_TEST(undefined_long_flag);
RUN_TEST(dangling_arg);
RUN_TEST(invalid_int);
RUN_TEST(huge_int);
}
SUITE(uglies) {
RUN_TEST(dangling_arg_oob);
RUN_TEST(arg_oob);
RUN_TEST(negative_argc);
RUN_TEST(null_argv);
}
int main(int argc, char **argv) {
GREATEST_MAIN_BEGIN();
RUN_SUITE(goods);
RUN_SUITE(bads);
RUN_SUITE(uglies);
GREATEST_MAIN_END();
}