CASE_TEST(crypto_cipher, aes_cfb_nopadding_encrypt) {
#if defined(CRYPTO_USE_OPENSSL) || defined(CRYPTO_USE_LIBRESSL) || defined(CRYPTO_USE_BORINGSSL)
    if (!openssl_test_inited) {
        openssl_test_inited = std::make_shared<openssl_test_init_wrapper>();
    }
#endif

    {
        util::crypto::cipher ci;
        CASE_EXPECT_EQ(0, ci.init("AES-256-CFB", ::util::crypto::cipher::mode_t::EN_CMODE_ENCRYPT));

        // CASE_EXPECT_EQ(16, ci.get_iv_size());
        // CASE_EXPECT_EQ(0, ci.set_iv(aes_test_cfb128_iv, 16));
        CASE_EXPECT_EQ(256, ci.get_key_bits());
        CASE_EXPECT_EQ(0, ci.set_key(aes_test_cfb128_key[2], 256));

        const size_t buffer_len = 29;

        for (int i = 0; i < 3; ++i) {
            unsigned char buf_in[64] = {0}, buf_out[128] = {0};
            size_t        olen = sizeof(buf_out);
            memcpy(buf_in, aes_test_cfb128_nopadding_pt[i], buffer_len);
            CASE_EXPECT_EQ(0, ci.encrypt(buf_in, buffer_len, buf_out, &olen));
            CASE_EXPECT_EQ(0, memcmp(buf_out, aes_test_cfb128_nopadding_ct[i], buffer_len));

            CASE_MSG_INFO() << "AES-256-CFB => txt: " << aes_test_cfb128_nopadding_pt[i] << std::endl;
            CASE_MSG_INFO() << "AES-256-CFB => enc: ";
            util::string::dumphex(buf_out, olen, std::cout);
            std::cout << std::endl;
        }
    }

    {
        util::crypto::cipher ci;
        CASE_EXPECT_EQ(0, ci.init("AES-256-CFB", ::util::crypto::cipher::mode_t::EN_CMODE_DECRYPT));

        // CASE_EXPECT_EQ(16, ci.get_iv_size());
        // CASE_EXPECT_EQ(0, ci.set_iv(aes_test_cfb128_iv, 16));
        CASE_EXPECT_EQ(256, ci.get_key_bits());
        CASE_EXPECT_EQ(0, ci.set_key(aes_test_cfb128_key[2], 256));

        const size_t buffer_len = 29;

        for (int i = 0; i < 3; ++i) {
            unsigned char buf_in[64] = {0}, buf_out[128] = {0};
            size_t        olen = sizeof(buf_out);
            memcpy(buf_in, aes_test_cfb128_nopadding_ct[i], buffer_len);
            CASE_EXPECT_EQ(0, ci.decrypt(buf_in, buffer_len, buf_out, &olen));
            CASE_EXPECT_EQ(0, memcmp(buf_out, aes_test_cfb128_nopadding_pt[i], buffer_len));

            CASE_MSG_INFO() << "AES-256-CFB => dec: ";
            util::string::dumphex(buf_in, buffer_len, std::cout);
            std::cout << std::endl;
            CASE_MSG_INFO() << "AES-256-CFB => txt: " << ((unsigned char *)buf_out) << std::endl;
        }
    }
}
CASE_TEST(ac_automation, skip) {
    util::string::ac_automation<> actree;

    actree.insert_keyword("艹");
    actree.insert_keyword("操你妈逼");
    actree.insert_keyword("你妈逼");
    actree.insert_keyword("艹你妈");
    actree.set_skip(' ');
    actree.set_skip('\t');
    actree.set_skip('\r');
    actree.set_skip('\n');

    std::string input = "小册老艹,我干死你操  你妈操  你妈\r\n逼艹 你妈";
    util::string::ac_automation<>::value_type res = actree.match(input);

// CI may not support this encoding
#ifndef _MSC_VER
    CASE_EXPECT_EQ(3, res.size());
#endif

    std::stringstream ss;
    size_t in_idx = 0;
    for (size_t i = 0; i < res.size(); ++i) {
        ss.write(&input[in_idx], res[i].start - in_idx);
        ss << "**";
        in_idx = res[i].start + res[i].length;
    }

    if (in_idx < input.size()) {
        ss.write(&input[in_idx], input.size() - in_idx);
    }

    CASE_MSG_INFO() << "filter resault: " << ss.str() << std::endl;
}
static void node_nodesync_test_on_debug(const char* file_path, size_t line, const atbus::node& n, const atbus::endpoint* ep, const atbus::connection* conn, const char* fmt, ...) {
    size_t offset = 0;
    for (size_t i = 0; file_path[i]; ++i) {
        if ('/' == file_path[i] || '\\' == file_path[i]) {
            offset = i + 1;
        }
    }
    file_path += offset;

    std::streamsize w = std::cout.width();
    CASE_MSG_INFO() << "[Log Debug][" << std::setw(24) << file_path << ":" << std::setw(4) << line << "] node=0x" << 
        std::setfill('0')<< std::hex<< std::setw(8)<< n.get_id() <<
        ", endpoint=0x" << std::setw(8)<< (NULL == ep ? 0 : ep->get_id()) <<
        ", connection=" << conn<< std::setfill(' ')<< std::setw(w)<<  std::dec<<
        "\t";

    va_list ap;
    va_start(ap, fmt);

    vprintf(fmt, ap);

    va_end(ap);

    puts("");
}
static void connected_callback_test_fn(
    atbus::channel::io_stream_channel* channel,         // 事件触发的channel
    atbus::channel::io_stream_connection* connection,   // 事件触发的连接
    int status,                         // libuv传入的转态码
    void*,                              // 额外参数(不同事件不同含义)
    size_t s                            // 额外参数长度
    ) {
    CASE_EXPECT_NE(NULL, channel);
    CASE_EXPECT_NE(NULL, connection);
    CASE_EXPECT_EQ(0, status);
    CASE_EXPECT_EQ(0, channel->error_code);

    if (0 != status) {
        CASE_MSG_INFO() << uv_err_name(channel->error_code) << ":"<< uv_strerror(channel->error_code) << std::endl;
    } else {
        CASE_MSG_INFO() << "connect to " << connection->addr.address<< " success" << std::endl;
    }

    ++g_check_flag;
}
static int setup_channel(atbus::channel::io_stream_channel& channel, const char* listen, const char* conn) {
    atbus::channel::channel_address_t addr;

    int res = 0;
    if (NULL != listen) {
        atbus::channel::make_address(listen, addr);
        res = atbus::channel::io_stream_listen(&channel, addr, listen_callback_test_fn, NULL, 0);
    } else {
        atbus::channel::make_address(conn, addr);
        res = atbus::channel::io_stream_connect(&channel, addr, connected_callback_test_fn, NULL, 0);
    }

    if (0 != res) {
        CASE_MSG_INFO() << uv_err_name(channel.error_code) << ":" << uv_strerror(channel.error_code) << std::endl;
        return 0;
    } else {
        return 1;
    }
}
CASE_TEST(crypto_cipher, get_all_cipher_names) {
#if defined(CRYPTO_USE_OPENSSL) || defined(CRYPTO_USE_LIBRESSL) || defined(CRYPTO_USE_BORINGSSL)
    if (!openssl_test_inited) {
        openssl_test_inited = std::make_shared<openssl_test_init_wrapper>();
    }
#endif

    const std::vector<std::string> &all_ciphers = util::crypto::cipher::get_all_cipher_names();
    std::stringstream               ss;
    for (size_t i = 0; i < all_ciphers.size(); ++i) {
        if (i) {
            ss << ",";
        }

        ss << all_ciphers[i];
    }

    CASE_MSG_INFO() << "All ciphers: " << ss.str() << std::endl;
    CASE_EXPECT_NE(0, all_ciphers.size());
}
static void node_reg_test_on_debug(const char* file_path, size_t line, 
    const atbus::node& n, const atbus::endpoint* ep, const atbus::connection* conn, 
    const atbus::protocol::msg* m,
    const char* fmt, ...) {
    size_t offset = 0;
    for (size_t i = 0; file_path[i]; ++i) {
        if ('/' == file_path[i] || '\\' == file_path[i]) {
            offset = i + 1;
        }
    }
    file_path += offset;

    std::streamsize w = std::cout.width();
    CASE_MSG_INFO() << "[Log Debug][" << std::setw(24) << file_path << ":" << std::setw(4) << line << "] node=0x" << 
        std::setfill('0')<< std::hex<< std::setw(8)<< n.get_id() <<
        ", endpoint=0x" << std::setw(8)<< (NULL == ep ? 0 : ep->get_id()) <<
        ", connection=" << conn<< std::setfill(' ')<< std::setw(w)<<  std::dec<<
        "\t";

    va_list ap;
    va_start(ap, fmt);

    vprintf(fmt, ap);

    va_end(ap);

    puts("");

#ifdef _MSC_VER

    static char* APPVEYOR = getenv("APPVEYOR");
    static char* CI = getenv("CI");
    
    // appveyor ci open msg content
    if (APPVEYOR && APPVEYOR[0] && CI && CI[0] && NULL != m) {
        std::cout << *m << std::endl;
    }
#endif
}
static void connect_failed_callback_test_fn(
    atbus::channel::io_stream_channel* channel,         // 事件触发的channel
    atbus::channel::io_stream_connection* connection,   // 事件触发的连接
    int status,                         // libuv传入的转态码
    void*,                              // 额外参数(不同事件不同含义)
    size_t s                            // 额外参数长度
    ) {
    CASE_EXPECT_NE(NULL, channel);
    CASE_EXPECT_EQ(NULL, connection);

    CASE_EXPECT_TRUE(EN_ATBUS_ERR_SOCK_CONNECT_FAILED == status || EN_ATBUS_ERR_DNS_GETADDR_FAILED == status);

    if (EN_ATBUS_ERR_SOCK_CONNECT_FAILED == status) {
        CASE_EXPECT_EQ(UV_ECONNREFUSED, channel->error_code);
    } else {
        CASE_EXPECT_EQ(UV_EAI_NONAME, channel->error_code);
    }

    if (0 != channel->error_code) {
        CASE_MSG_INFO() << uv_err_name(channel->error_code) << ":" << uv_strerror(channel->error_code) << std::endl;
    }
         
    ++g_check_flag;
}
CASE_TEST(channel, io_stream_tcp_basic)
{
    atbus::adapter::loop_t loop;
    uv_loop_init(&loop);

    atbus::channel::io_stream_channel svr, cli;
    atbus::channel::io_stream_init(&svr, &loop, NULL);
    atbus::channel::io_stream_init(&cli, &loop, NULL);
    CASE_EXPECT_EQ(&loop, svr.ev_loop);
    CASE_EXPECT_EQ(&loop, cli.ev_loop);

    g_check_flag = 0;

    int inited_fds = 0;
    inited_fds += setup_channel(svr, "ipv6://:::16387", NULL);
    CASE_EXPECT_EQ(1, g_check_flag);
    CASE_EXPECT_NE(NULL, svr.ev_loop);
    
    if (0 == inited_fds) {
        uv_loop_close(&loop);
        return;
    }

    inited_fds = 0;
    inited_fds += setup_channel(cli, NULL, "ipv4://127.0.0.1:16387");
    inited_fds += setup_channel(cli, NULL, "dns://localhost:16387");
    inited_fds += setup_channel(cli, NULL, "ipv6://::1:16387");

    int check_flag = g_check_flag;
    while (g_check_flag - check_flag < 2 * inited_fds) {
        uv_run(&loop, UV_RUN_ONCE);
    }

    svr.evt.callbacks[atbus::channel::io_stream_callback_evt_t::EN_FN_RECVED] = recv_callback_check_fn;
    cli.evt.callbacks[atbus::channel::io_stream_callback_evt_t::EN_FN_RECVED] = recv_callback_check_fn;
    char* buf = get_test_buffer();

    check_flag = g_check_flag;
    // small buffer
    atbus::channel::io_stream_send(cli.conn_pool.begin()->second.get(), buf, 13);
    g_check_buff_sequence.push_back(std::make_pair(0, 13));
    atbus::channel::io_stream_send(cli.conn_pool.begin()->second.get(), buf + 13, 28);
    g_check_buff_sequence.push_back(std::make_pair(13, 28));
    atbus::channel::io_stream_send(cli.conn_pool.begin()->second.get(), buf + 13 + 28, 100);
    g_check_buff_sequence.push_back(std::make_pair(13 + 28, 100));

    // big buffer
    atbus::channel::io_stream_send(cli.conn_pool.begin()->second.get(), buf + 1024, 56 * 1024 + 3);
    g_check_buff_sequence.push_back(std::make_pair(1024, 56 * 1024 + 3));

    while (g_check_flag - check_flag < 4) {
        uv_run(&loop, UV_RUN_ONCE);
    }

    // many big buffer
    {
        check_flag = g_check_flag;
        atbus::channel::io_stream_channel::conn_pool_t::iterator it = svr.conn_pool.begin();
        // 跳过listen的socket
        if (it->second->addr.address == "ipv6://:::16387") {
            ++it;
        }

        size_t sum_size = 0;
        g_recv_rec = std::make_pair(0, 0);
        for (int i = 0; i < 153; ++ i) {
            size_t s = static_cast<size_t>(rand() % 2048);
            size_t l = static_cast<size_t>(rand() % 10240) + 20 * 1024;
            atbus::channel::io_stream_send(it->second.get(), buf + s, l);
            g_check_buff_sequence.push_back(std::make_pair(s, l));
            sum_size += l;
        }

        CASE_MSG_INFO() << "send " << sum_size << " bytes data with " << g_check_buff_sequence.size() << " packages done." << std::endl;

        while (g_check_flag - check_flag < 153) {
            uv_run(&loop, UV_RUN_ONCE);
        }

        CASE_MSG_INFO() << "recv " << g_recv_rec.second << " bytes data with " << g_recv_rec.first << " packages and checked done." << std::endl;
    }

    atbus::channel::io_stream_close(&svr);
    atbus::channel::io_stream_close(&cli);
    CASE_EXPECT_EQ(0, svr.conn_pool.size());
    CASE_EXPECT_EQ(0, cli.conn_pool.size());

    uv_loop_close(&loop);
}