blob: e8020f77fb2d422b908b1389313fb947e2ff858e [file] [log] [blame] [edit]
/**
* Copyright © 2019 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include "util.h"
static ssize_t __read(int fd, void *buf, size_t len);
#define read __read
#include "config.c"
#include "console-socket.c"
#define main __main
#include "console-client.c"
#undef read
#undef main
struct test {
enum esc_type esc_type;
union {
struct ssh_esc_state ssh;
struct str_esc_state str;
} esc_state;
const char *in[4];
size_t n_in;
const char *exp_out;
int exp_rc;
};
struct test_ctx {
struct console_client client;
struct test *test;
uint8_t out[4096];
size_t cur_in;
size_t cur_out;
};
struct test tests[] = {
{
/* no escape code */
.esc_type = ESC_TYPE_SSH,
.in = { "a" },
.n_in = 1,
.exp_out = "a",
.exp_rc = PROCESS_EXIT,
},
{
/* no escape code, multiple reads */
.esc_type = ESC_TYPE_SSH,
.in = { "a", "b" },
.n_in = 2,
.exp_out = "ab",
.exp_rc = PROCESS_EXIT,
},
{
/* ssh escape in one read */
.esc_type = ESC_TYPE_SSH,
.in = { "a\r~." },
.n_in = 1,
.exp_out = "a\r",
.exp_rc = PROCESS_ESC,
},
{
/* ssh escape, partial ~ is not output. */
.esc_type = ESC_TYPE_SSH,
.in = { "a\r~" },
.n_in = 1,
.exp_out = "a\r",
.exp_rc = PROCESS_EXIT,
},
{
/* ssh escape split into individual reads */
.esc_type = ESC_TYPE_SSH,
.in = { "a", "\r", "~", "." },
.n_in = 4,
.exp_out = "a\r",
.exp_rc = PROCESS_ESC,
},
{
/* ssh escape, escaped. */
.esc_type = ESC_TYPE_SSH,
.in = { "a\r~~." },
.n_in = 1,
.exp_out = "a\r~.",
.exp_rc = PROCESS_EXIT,
},
{
/* ssh escape, escaped ~, and not completed. */
.esc_type = ESC_TYPE_SSH,
.in = { "a\r~~" },
.n_in = 1,
.exp_out = "a\r~",
.exp_rc = PROCESS_EXIT,
},
{
/* str escape, no match */
.esc_type = ESC_TYPE_STR,
.esc_state = { .str = { .str = (const uint8_t *)"c" } },
.in = { "ab" },
.n_in = 1,
.exp_out = "ab",
.exp_rc = PROCESS_EXIT,
},
{
/* str escape, one byte, as one read */
.esc_type = ESC_TYPE_STR,
.esc_state = { .str = { .str = (const uint8_t *)"b" } },
.in = { "abc" },
.n_in = 1,
.exp_out = "ab",
.exp_rc = PROCESS_ESC,
},
{
/* str escape, multiple bytes, as one read */
.esc_type = ESC_TYPE_STR,
.esc_state = { .str = { .str = (const uint8_t *)"bc" } },
.in = { "abcd" },
.n_in = 1,
.exp_out = "abc",
.exp_rc = PROCESS_ESC,
},
{
/* str escape, multiple bytes, split over reads */
.esc_type = ESC_TYPE_STR,
.esc_state = { .str = { .str = (const uint8_t *)"bc" } },
.in = { "ab", "cd" },
.n_in = 2,
.exp_out = "abc",
.exp_rc = PROCESS_ESC,
},
{
/* str escape, not matched due to intermediate data */
.esc_type = ESC_TYPE_STR,
.esc_state = { .str = { .str = (const uint8_t *)"ab" } },
.in = { "acb" },
.n_in = 1,
.exp_out = "acb",
.exp_rc = PROCESS_EXIT,
},
};
struct test_ctx ctxs[ARRAY_SIZE(tests)];
int write_buf_to_fd(int fd, const uint8_t *buf, size_t len)
{
struct test_ctx *ctx = &ctxs[fd];
assert(ctx->cur_out + len <= sizeof(ctx->out));
memcpy(ctx->out + ctx->cur_out, buf, len);
ctx->cur_out += len;
return 0;
}
static ssize_t __read(int fd, void *buf, size_t len)
{
struct test_ctx *ctx = &ctxs[fd];
const char *inbuf;
size_t inlen;
if (len > SSIZE_MAX) {
errno = EINVAL;
return -1;
}
if (ctx->cur_in >= ctx->test->n_in) {
return 0;
}
inbuf = ctx->test->in[ctx->cur_in];
inlen = strlen(inbuf);
assert(inlen <= len);
memcpy(buf, inbuf, inlen);
ctx->cur_in++;
return (ssize_t)inlen;
}
void run_one_test(size_t idx, struct test *test, struct test_ctx *ctx)
{
size_t exp_out_len;
int rc;
/* we store the index into the context array as a FD, so we
* can refer to it through the read & write callbacks.
*/
assert(idx < INT_MAX);
ctx->client.console_sd = (int)idx;
ctx->client.fd_in = (int)idx;
ctx->client.esc_type = test->esc_type;
memcpy(&ctx->client.esc_state, &test->esc_state,
sizeof(test->esc_state));
ctx->test = test;
for (;;) {
rc = process_tty(&ctx->client);
if (rc != PROCESS_OK) {
break;
}
}
exp_out_len = strlen(test->exp_out);
#ifdef DEBUG
printf("got: rc %d %s(%d), exp: rc %d %s(%ld)\n", rc, ctx->out,
ctx->cur_out, test->exp_rc, test->exp_out, exp_out_len);
fflush(stdout);
#endif
assert(rc == test->exp_rc);
assert(exp_out_len == ctx->cur_out);
assert(!memcmp(ctx->out, test->exp_out, exp_out_len));
}
int main(void)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(tests); i++) {
run_one_test(i, &tests[i], &ctxs[i]);
}
return EXIT_SUCCESS;
}