diff --git a/README.md b/README.md
index dc62dccfcbe..f76e0090fe0 100755
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@
[](https://discord.gg/yZ4BnPmHAd)
[](https://opencollective.com/srs-server)
[](https://hub.docker.com/r/ossrs/srs/tags)
+[](https://codecov.io/gh/ossrs/srs)
SRS/7.0 ([Kai](https://ossrs.io/lts/en-us/product#release-70)) is a simple, high-efficiency, and real-time video server,
supporting RTMP/WebRTC/HLS/HTTP-FLV/SRT/MPEG-DASH/GB28181, Linux/Windows/macOS, X86_64/ARMv7/AARCH64/M1/RISCV/LOONGARCH/MIPS,
diff --git a/trunk/auto/codecov.sh b/trunk/auto/codecov.sh
index c6b9a61d5d4..71256436e01 100755
--- a/trunk/auto/codecov.sh
+++ b/trunk/auto/codecov.sh
@@ -5,8 +5,10 @@
# and generate *.gcda by
# ./objs/srs_utest
-# Workdir is objs/cover.
-workdir=$(cd `dirname $0`/.. && pwd)/objs/cover
+# Workdir should be in the same directory, for example:
+# ./objs/srs_utest
+# bash <(curl -s https://codecov.io/bash)
+workdir=$(cd `dirname $0`/.. && pwd)
# Create trunk under workdir.
mkdir -p $workdir && cd $workdir
diff --git a/trunk/configure b/trunk/configure
index b8cd215e4aa..ca4cdfb6bdc 100755
--- a/trunk/configure
+++ b/trunk/configure
@@ -251,7 +251,7 @@ MODULE_FILES=("srs_kernel_error" "srs_kernel_log" "srs_kernel_buffer"
"srs_kernel_consts" "srs_kernel_aac" "srs_kernel_mp3" "srs_kernel_ts" "srs_kernel_ps"
"srs_kernel_stream" "srs_kernel_balance" "srs_kernel_mp4" "srs_kernel_file"
"srs_kernel_kbps" "srs_kernel_rtc_rtp" "srs_kernel_rtc_rtcp" "srs_kernel_packet"
- "srs_kernel_uuid" "srs_kernel_st" "srs_kernel_factory" "srs_kernel_hourglass"
+ "srs_kernel_st" "srs_kernel_factory" "srs_kernel_hourglass"
"srs_kernel_pithy_print" "srs_kernel_rtc_queue" "srs_kernel_resource")
KERNEL_INCS="src/kernel"; MODULE_DIR=${KERNEL_INCS} . $SRS_WORKDIR/auto/modules.sh
KERNEL_OBJS="${MODULE_OBJS[@]}"
@@ -381,7 +381,7 @@ if [[ $SRS_UTEST == YES ]]; then
"srs_utest_protocol" "srs_utest_protocol2" "srs_utest_kernel2" "srs_utest_protocol3"
"srs_utest_st" "srs_utest_rtc2" "srs_utest_rtc3" "srs_utest_fmp4" "srs_utest_source_lock"
"srs_utest_stream_token" "srs_utest_rtc_recv_track" "srs_utest_st2" "srs_utest_hevc_structs"
- "srs_utest_coworkers" "srs_utest_pithy_print")
+ "srs_utest_coworkers" "srs_utest_pithy_print" "srs_utest_kernel3")
# Always include SRT utest
MODULE_FILES+=("srs_utest_srt")
if [[ $SRS_GB28181 == YES ]]; then
diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md
index 83a79afd28a..341b74ef97f 100644
--- a/trunk/doc/CHANGELOG.md
+++ b/trunk/doc/CHANGELOG.md
@@ -7,6 +7,7 @@ The changelog for SRS.
## SRS 7.0 Changelog
+* v7.0, 2025-09-14, Merge [#4488](https://github.com/ossrs/srs/pull/4488): AI: Add utests for kernel and protocol. v7.0.87 (#4488)
* v7.0, 2025-09-13, Merge [#4486](https://github.com/ossrs/srs/pull/4486): Move some app files to kernel. v7.0.86 (#4486)
* v7.0, 2025-09-12, Merge [#4485](https://github.com/ossrs/srs/pull/4485): AI: Fix naming problem for app module. v7.0.85 (#4485)
* v7.0, 2025-09-09, Merge [#4446](https://github.com/ossrs/srs/pull/4446): SRT2RTMP: fix srt bridge hevc to rtmp error. v7.0.84 (#4446)
diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp
index 3f47aea396a..6467c65b78a 100644
--- a/trunk/src/core/srs_core_version7.hpp
+++ b/trunk/src/core/srs_core_version7.hpp
@@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
-#define VERSION_REVISION 86
+#define VERSION_REVISION 87
#endif
\ No newline at end of file
diff --git a/trunk/src/kernel/srs_kernel_uuid.cpp b/trunk/src/kernel/srs_kernel_uuid.cpp
deleted file mode 100644
index 7994fcaf6a4..00000000000
--- a/trunk/src/kernel/srs_kernel_uuid.cpp
+++ /dev/null
@@ -1,1314 +0,0 @@
-//
-// libuuid BSD License @see https://sourceforge.net/projects/libuuid/
-//
-// SPDX-License-Identifier: BSD-3-Clause
-//
-
-#include
-
-#include
-#include
-#include
-#define HAVE_USLEEP
-////////////////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////////////////
-
-/*
- * Fundamental C definitions.
- */
-
-#ifndef UTIL_LINUX_C_H
-#define UTIL_LINUX_C_H
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include
-#include
-
-#ifdef HAVE_STDINT_H
-#include
-#else
-#ifdef HAVE_INTTYPES_H
-#include
-#endif
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef HAVE_ERR_H
-#include
-#endif
-
-#ifndef HAVE_USLEEP
-#include
-#endif
-
-/*
- * Compiler specific stuff
- */
-#ifndef __GNUC_PREREQ
-#if defined __GNUC__ && defined __GNUC_MINOR__
-#define __GNUC_PREREQ(maj, min) \
- ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
-#else
-#define __GNUC_PREREQ(maj, min) 0
-#endif
-#endif
-
-#ifdef __GNUC__
-
-/* &a[0] degrades to a pointer: a different type from an array */
-#define __must_be_array(a) \
- UL_BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(__typeof__(a), __typeof__(&a[0])))
-
-#define ignore_result(x) ({ \
- __typeof__(x) __dummy __attribute__((__unused__)) = (x); \
- (void)__dummy; \
-})
-
-#else /* !__GNUC__ */
-#define __must_be_array(a) 0
-#define __attribute__(_arg_)
-#define ignore_result(x) ((void)(x))
-#endif /* !__GNUC__ */
-
-/*
- * Function attributes
- */
-#ifndef __ul_alloc_size
-#if __GNUC_PREREQ(4, 3)
-#define __ul_alloc_size(s) __attribute__((alloc_size(s)))
-#else
-#define __ul_alloc_size(s)
-#endif
-#endif
-
-#ifndef __ul_calloc_size
-#if __GNUC_PREREQ(4, 3)
-#define __ul_calloc_size(n, s) __attribute__((alloc_size(n, s)))
-#else
-#define __ul_calloc_size(n, s)
-#endif
-#endif
-
-/* Force a compilation error if condition is true, but also produce a
- * result (of value 0 and type size_t), so the expression can be used
- * e.g. in a structure initializer (or where-ever else comma expressions
- * aren't permitted).
- */
-#define UL_BUILD_BUG_ON_ZERO(e) (sizeof(struct { int : -!!(e); }))
-#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int : -!!(e); }))
-
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
-#endif
-
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-#ifndef min
-#define min(x, y) ({ \
- __typeof__(x) _min1 = (x); \
- __typeof__(y) _min2 = (y); \
- (void) (&_min1 == &_min2); \
- _min1 < _min2 ? _min1 : _min2; })
-#endif
-
-#ifndef max
-#define max(x, y) ({ \
- __typeof__(x) _max1 = (x); \
- __typeof__(y) _max2 = (y); \
- (void) (&_max1 == &_max2); \
- _max1 > _max2 ? _max1 : _max2; })
-#endif
-
-#ifndef offsetof
-#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
-#endif
-
-#ifndef container_of
-#define container_of(ptr, type, member) ({ \
- const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) ); })
-#endif
-
-#if 0
-#ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME
-#ifdef HAVE___PROGNAME
-extern char *__progname;
-#define program_invocation_short_name __progname
-#else
-#ifdef HAVE_GETEXECNAME
-#define program_invocation_short_name \
- prog_inv_sh_nm_from_file(getexecname(), 0)
-#else
-#define program_invocation_short_name \
- prog_inv_sh_nm_from_file((char *)__FILE__, 1)
-#endif
-static char prog_inv_sh_nm_buf[256];
-static inline char *
-prog_inv_sh_nm_from_file(char *f, char stripext)
-{
- char *t;
-
- if ((t = strrchr(f, '/')) != NULL)
- t++;
- else
- t = f;
-
- strncpy(prog_inv_sh_nm_buf, t, sizeof(prog_inv_sh_nm_buf) - 1);
- prog_inv_sh_nm_buf[sizeof(prog_inv_sh_nm_buf) - 1] = '\0';
-
- if (stripext && (t = strrchr(prog_inv_sh_nm_buf, '.')) != NULL)
- *t = '\0';
-
- return prog_inv_sh_nm_buf;
-}
-#endif
-#endif
-#endif
-
-#ifndef HAVE_ERR_H
-#if 0
-static inline void
-errmsg(char doexit, int excode, char adderr, const char *fmt, ...)
-{
- fprintf(stderr, "%s: ", program_invocation_short_name);
- if (fmt != NULL) {
- va_list argp;
- va_start(argp, fmt);
- vfprintf(stderr, fmt, argp);
- va_end(argp);
- if (adderr)
- fprintf(stderr, ": ");
- }
- if (adderr)
- fprintf(stderr, "%m");
- fprintf(stderr, "\n");
- if (doexit)
- exit(excode);
-}
-#endif
-
-#ifndef HAVE_ERR
-#define err(E, FMT...) errmsg(1, E, 1, FMT)
-#endif
-
-#ifndef HAVE_ERRX
-#define errx(E, FMT...) errmsg(1, E, 0, FMT)
-#endif
-
-#ifndef HAVE_WARN
-#define warn(FMT...) errmsg(0, 0, 1, FMT)
-#endif
-
-#ifndef HAVE_WARNX
-#define warnx(FMT...) errmsg(0, 0, 0, FMT)
-#endif
-#endif /* !HAVE_ERR_H */
-
-#if 0
-static inline __attribute__((const)) int is_power_of_2(unsigned long num)
-{
- return (num != 0 && ((num & (num - 1)) == 0));
-}
-#endif
-
-#ifndef HAVE_LOFF_T
-typedef int64_t loff_t;
-#endif
-
-#if !defined(HAVE_DIRFD) && (!defined(HAVE_DECL_DIRFD) || HAVE_DECL_DIRFD == 0) && defined(HAVE_DIR_DD_FD)
-#include
-#include
-static inline int dirfd(DIR *d)
-{
- return d->dd_fd;
-}
-#endif
-
-/*
- * Fallback defines for old versions of glibc
- */
-#include
-
-#ifdef O_CLOEXEC
-#define UL_CLOEXECSTR "e"
-#else
-#define UL_CLOEXECSTR ""
-#endif
-
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
-#endif
-
-#ifndef AI_ADDRCONFIG
-#define AI_ADDRCONFIG 0x0020
-#endif
-
-#ifndef IUTF8
-#define IUTF8 0040000
-#endif
-
-#if 0
-/*
- * MAXHOSTNAMELEN replacement
- */
-static inline size_t get_hostname_max(void)
-{
-#if HAVE_DECL__SC_HOST_NAME_MAX
- long len = sysconf(_SC_HOST_NAME_MAX);
-
- if (0 < len)
- return len;
-#endif
-
-#ifdef MAXHOSTNAMELEN
- return MAXHOSTNAMELEN;
-#elif HOST_NAME_MAX
- return HOST_NAME_MAX;
-#endif
- return 64;
-}
-#endif
-
-#ifndef HAVE_USLEEP
-/*
- * This function is marked obsolete in POSIX.1-2001 and removed in
- * POSIX.1-2008. It is replaced with nanosleep().
- */
-static inline int usleep(useconds_t usec)
-{
- struct timespec waittime = {
- .tv_sec = usec / 1000000L,
- .tv_nsec = (usec % 1000000L) * 1000};
- return nanosleep(&waittime, NULL);
-}
-#endif
-
-/*
- * Constant strings for usage() functions. For more info see
- * Documentation/howto-usage-function.txt and disk-utils/delpart.c
- */
-#define USAGE_HEADER _("\nUsage:\n")
-#define USAGE_OPTIONS _("\nOptions:\n")
-#define USAGE_SEPARATOR _("\n")
-#define USAGE_HELP _(" -h, --help display this help and exit\n")
-#define USAGE_VERSION _(" -V, --version output version information and exit\n")
-#define USAGE_MAN_TAIL(_man) _("\nFor more details see %s.\n"), _man
-
-#define UTIL_LINUX_VERSION _("%s from %s\n"), program_invocation_short_name, PACKAGE_STRING
-
-/*
- * scanf modifiers for "strings allocation"
- */
-#ifdef HAVE_SCANF_MS_MODIFIER
-#define UL_SCNsA "%ms"
-#elif defined(HAVE_SCANF_AS_MODIFIER)
-#define UL_SCNsA "%as"
-#endif
-
-/*
- * seek stuff
- */
-#ifndef SEEK_DATA
-#define SEEK_DATA 3
-#endif
-#ifndef SEEK_HOLE
-#define SEEK_HOLE 4
-#endif
-
-#endif /* UTIL_LINUX_C_H */
-
-/*
- * Definitions used by the uuidd daemon
- *
- * Copyright (C) 2007 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#ifndef _UUID_UUIDD_H
-#define _UUID_UUIDD_H
-
-#define UUIDD_DIR _PATH_LOCALSTATEDIR "/uuidd"
-#define UUIDD_SOCKET_PATH UUIDD_DIR "/request"
-#define UUIDD_PIDFILE_PATH UUIDD_DIR "/uuidd.pid"
-#define UUIDD_PATH "/usr/sbin/uuidd"
-
-#define UUIDD_OP_GETPID 0
-#define UUIDD_OP_GET_MAXOP 1
-#define UUIDD_OP_TIME_UUID 2
-#define UUIDD_OP_RANDOM_UUID 3
-#define UUIDD_OP_BULK_TIME_UUID 4
-#define UUIDD_OP_BULK_RANDOM_UUID 5
-#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID
-
-extern int __uuid_generate_time(uuid_t out, int *num);
-extern void __uuid_generate_random(uuid_t out, int *num);
-
-#endif /* _UUID_UUID_H */
-
-#ifndef UTIL_LINUX_RANDUTILS
-#define UTIL_LINUX_RANDUTILS
-
-#ifdef HAVE_SRANDOM
-#define srand(x) srandom(x)
-#define rand() random()
-#endif
-
-extern int random_get_fd(void);
-extern void random_get_bytes(void *buf, size_t nbytes);
-
-#endif
-
-/*
- * uuid.h -- private header file for uuids
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include
-#include
-
-// #include "uuid.h"
-
-#define LIBUUID_CLOCK_FILE "/var/lib/libuuid/clock.txt"
-
-/*
- * Offset between 15-Oct-1582 and 1-Jan-70
- */
-#define TIME_OFFSET_HIGH 0x01B21DD2
-#define TIME_OFFSET_LOW 0x13814000
-
-struct uuid {
- uint32_t time_low;
- uint16_t time_mid;
- uint16_t time_hi_and_version;
- uint16_t clock_seq;
- uint8_t node[6];
-};
-
-/*
- * prototypes
- */
-void uuid_pack(const struct uuid *uu, uuid_t ptr);
-void uuid_unpack(const uuid_t in, struct uuid *uu);
-
-/*
- * gen_uuid.c --- generate a DCE-compatible uuid
- *
- * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-/*
- * Force inclusion of SVID stuff since we need it if we're compiling in
- * gcc-wall wall mode
- */
-#ifndef _SVID_SOURCE
-#define _SVID_SOURCE
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#ifdef HAVE_SYS_TIME_H
-#include
-#endif
-#include
-#ifdef HAVE_SYS_FILE_H
-#include
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include
-#endif
-#ifdef HAVE_SYS_SOCKET_H
-#include
-#endif
-#ifdef HAVE_SYS_UN_H
-#include
-#endif
-#ifdef HAVE_SYS_SOCKIO_H
-#include
-#endif
-#ifdef HAVE_NET_IF_H
-#include
-#endif
-#ifdef HAVE_NETINET_IN_H
-#include
-#endif
-#ifdef HAVE_NET_IF_DL_H
-#include
-#endif
-#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
-#include
-#endif
-
-// #include "all-io.h"
-// #include "uuidP.h"
-// #include "uuidd.h"
-// #include "randutils.h"
-// #include "c.h"
-
-#ifdef HAVE_TLS
-#define THREAD_LOCAL static __thread
-#else
-#define THREAD_LOCAL static
-#endif
-
-#ifndef LOCK_EX
-/* flock() replacement */
-#define LOCK_EX 1
-#define LOCK_SH 2
-#define LOCK_UN 3
-#define LOCK_NB 4
-
-static int flock(int fd, int op)
-{
- int rc = 0;
-
-#if defined(F_SETLK) && defined(F_SETLKW)
- struct flock fl = {0};
-
- switch (op & (LOCK_EX | LOCK_SH | LOCK_UN)) {
- case LOCK_EX:
- fl.l_type = F_WRLCK;
- break;
-
- case LOCK_SH:
- fl.l_type = F_RDLCK;
- break;
-
- case LOCK_UN:
- fl.l_type = F_UNLCK;
- break;
-
- default:
- errno = EINVAL;
- return -1;
- }
-
- fl.l_whence = SEEK_SET;
- rc = fcntl(fd, op & LOCK_NB ? F_SETLK : F_SETLKW, &fl);
-
- if (rc && (errno == EAGAIN))
- errno = EWOULDBLOCK;
-#endif /* defined(F_SETLK) && defined(F_SETLKW) */
-
- return rc;
-}
-
-#endif /* LOCK_EX */
-
-#if defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H)
-// Helper function for reading all bytes
-static ssize_t read_all(int fd, char *buf, size_t count)
-{
- ssize_t ret;
- ssize_t c = 0;
- int tries = 0;
-
- memset(buf, 0, count);
- while (count > 0) {
- ret = read(fd, buf, count);
- if (ret <= 0) {
- if ((errno == EAGAIN || errno == EINTR) && (tries++ < 5))
- continue;
- return c ? c : -1;
- }
- if (ret > 0) {
- tries = 0;
- count -= ret;
- buf += ret;
- c += ret;
- }
- }
- return c;
-}
-#endif
-
-/*
- * Get the ethernet hardware address, if we can find it...
- *
- * XXX for a windows version, probably should use GetAdaptersInfo:
- * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451
- * commenting out get_node_id just to get gen_uuid to compile under windows
- * is not the right way to go!
- */
-static int get_node_id(unsigned char *node_id)
-{
-#ifdef HAVE_NET_IF_H
- int sd;
- struct ifreq ifr, *ifrp;
- struct ifconf ifc;
- char buf[1024];
- int n, i;
- unsigned char *a;
-#ifdef HAVE_NET_IF_DL_H
- struct sockaddr_dl *sdlp;
-#endif
-
-/*
- * BSD 4.4 defines the size of an ifreq to be
- * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
- * However, under earlier systems, sa_len isn't present, so the size is
- * just sizeof(struct ifreq)
- */
-#ifdef HAVE_SA_LEN
-#define ifreq_size(i) max(sizeof(struct ifreq), \
- sizeof((i).ifr_name) + (i).ifr_addr.sa_len)
-#else
-#define ifreq_size(i) sizeof(struct ifreq)
-#endif /* HAVE_SA_LEN */
-
- sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
- if (sd < 0) {
- return -1;
- }
- memset(buf, 0, sizeof(buf));
- ifc.ifc_len = sizeof(buf);
- ifc.ifc_buf = buf;
- if (ioctl(sd, SIOCGIFCONF, (char *)&ifc) < 0) {
- close(sd);
- return -1;
- }
- n = ifc.ifc_len;
- for (i = 0; i < n; i += ifreq_size(*ifrp)) {
- ifrp = (struct ifreq *)((char *)ifc.ifc_buf + i);
- strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
-#ifdef SIOCGIFHWADDR
- if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
- continue;
- a = (unsigned char *)&ifr.ifr_hwaddr.sa_data;
-#else
-#ifdef SIOCGENADDR
- if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
- continue;
- a = (unsigned char *)ifr.ifr_enaddr;
-#else
-#ifdef HAVE_NET_IF_DL_H
- sdlp = (struct sockaddr_dl *)&ifrp->ifr_addr;
- if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6))
- continue;
- a = (unsigned char *)&sdlp->sdl_data[sdlp->sdl_nlen];
-#else
- /*
- * XXX we don't have a way of getting the hardware
- * address
- */
- close(sd);
- return 0;
-#endif /* HAVE_NET_IF_DL_H */
-#endif /* SIOCGENADDR */
-#endif /* SIOCGIFHWADDR */
- if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
- continue;
- if (node_id) {
- memcpy(node_id, a, 6);
- close(sd);
- return 1;
- }
- }
- close(sd);
-#endif
- return 0;
-}
-
-/* Assume that the gettimeofday() has microsecond granularity */
-#define MAX_ADJUSTMENT 10
-
-/*
- * Get clock from global sequence clock counter.
- *
- * Return -1 if the clock counter could not be opened/locked (in this case
- * pseudorandom value is returned in @ret_clock_seq), otherwise return 0.
- */
-static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
- uint16_t *ret_clock_seq, int *num)
-{
- THREAD_LOCAL int adjustment = 0;
- THREAD_LOCAL struct timeval last = {0, 0};
- THREAD_LOCAL int state_fd = -2;
- THREAD_LOCAL FILE *state_f;
- THREAD_LOCAL uint16_t clock_seq;
- struct timeval tv;
- uint64_t clock_reg;
- mode_t save_umask;
- int len;
- int ret = 0;
-
- if (state_fd == -2) {
- save_umask = umask(0);
- state_fd = open(LIBUUID_CLOCK_FILE, O_RDWR | O_CREAT | O_CLOEXEC, 0660);
- (void)umask(save_umask);
- if (state_fd != -1) {
- state_f = fdopen(state_fd, "r+" UL_CLOEXECSTR);
- if (!state_f) {
- close(state_fd);
- state_fd = -1;
- ret = -1;
- }
- } else
- ret = -1;
- }
- if (state_fd >= 0) {
- rewind(state_f);
- while (flock(state_fd, LOCK_EX) < 0) {
- if ((errno == EAGAIN) || (errno == EINTR))
- continue;
- fclose(state_f);
- close(state_fd);
- state_fd = -1;
- ret = -1;
- break;
- }
- }
- if (state_fd >= 0) {
- unsigned int cl;
- unsigned long tv1, tv2;
- int a;
-
- if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n",
- &cl, &tv1, &tv2, &a) == 4) {
- clock_seq = cl & 0x3FFF;
- last.tv_sec = tv1;
- last.tv_usec = tv2;
- adjustment = a;
- }
- }
-
- if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
- random_get_bytes(&clock_seq, sizeof(clock_seq));
- clock_seq &= 0x3FFF;
- gettimeofday(&last, 0);
- last.tv_sec--;
- }
-
-try_again:
- gettimeofday(&tv, 0);
- if ((tv.tv_sec < last.tv_sec) ||
- ((tv.tv_sec == last.tv_sec) &&
- (tv.tv_usec < last.tv_usec))) {
- clock_seq = (clock_seq + 1) & 0x3FFF;
- adjustment = 0;
- last = tv;
- } else if ((tv.tv_sec == last.tv_sec) &&
- (tv.tv_usec == last.tv_usec)) {
- if (adjustment >= MAX_ADJUSTMENT)
- goto try_again;
- adjustment++;
- } else {
- adjustment = 0;
- last = tv;
- }
-
- clock_reg = tv.tv_usec * 10 + adjustment;
- clock_reg += ((uint64_t)tv.tv_sec) * 10000000;
- clock_reg += (((uint64_t)0x01B21DD2) << 32) + 0x13814000;
-
- if (num && (*num > 1)) {
- adjustment += *num - 1;
- last.tv_usec += adjustment / 10;
- adjustment = adjustment % 10;
- last.tv_sec += last.tv_usec / 1000000;
- last.tv_usec = last.tv_usec % 1000000;
- }
-
- if (state_fd >= 0) {
- rewind(state_f);
- len = fprintf(state_f,
- "clock: %04x tv: %016lu %08lu adj: %08d\n",
- clock_seq, last.tv_sec, (unsigned long)last.tv_usec, adjustment);
- fflush(state_f);
- if (ftruncate(state_fd, len) < 0) {
- fprintf(state_f, " \n");
- fflush(state_f);
- }
- rewind(state_f);
- flock(state_fd, LOCK_UN);
- }
-
- *clock_high = clock_reg >> 32;
- *clock_low = clock_reg;
- *ret_clock_seq = clock_seq;
- return ret;
-}
-
-#if defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H)
-/*
- * Try using the uuidd daemon to generate the UUID
- *
- * Returns 0 on success, non-zero on failure.
- */
-static int get_uuid_via_daemon(int op, uuid_t out, int *num)
-{
- char op_buf[64];
- int op_len;
- int s;
- ssize_t ret;
- int32_t reply_len = 0, expected = 16;
- struct sockaddr_un srv_addr;
-
- if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
- return -1;
-
- srv_addr.sun_family = AF_UNIX;
- strcpy(srv_addr.sun_path, UUIDD_SOCKET_PATH);
-
- if (connect(s, (const struct sockaddr *)&srv_addr,
- sizeof(struct sockaddr_un)) < 0)
- goto fail;
-
- op_buf[0] = op;
- op_len = 1;
- if (op == UUIDD_OP_BULK_TIME_UUID) {
- memcpy(op_buf + 1, num, sizeof(*num));
- op_len += sizeof(*num);
- expected += sizeof(*num);
- }
-
- ret = write(s, op_buf, op_len);
- if (ret < 1)
- goto fail;
-
- ret = read_all(s, (char *)&reply_len, sizeof(reply_len));
- if (ret < 0)
- goto fail;
-
- if (reply_len != expected)
- goto fail;
-
- ret = read_all(s, op_buf, reply_len);
-
- if (op == UUIDD_OP_BULK_TIME_UUID)
- memcpy(op_buf + 16, num, sizeof(int));
-
- memcpy(out, op_buf, 16);
-
- close(s);
- return ((ret == expected) ? 0 : -1);
-
-fail:
- close(s);
- return -1;
-}
-
-#else /* !defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H) */
-static int get_uuid_via_daemon(int op, uuid_t out, int *num)
-{
- return -1;
-}
-#endif
-
-int __uuid_generate_time(uuid_t out, int *num)
-{
- static unsigned char node_id[6];
- static int has_init = 0;
- struct uuid uu;
- uint32_t clock_mid;
- int ret;
-
- if (!has_init) {
- if (get_node_id(node_id) <= 0) {
- random_get_bytes(node_id, 6);
- /*
- * Set multicast bit, to prevent conflicts
- * with IEEE 802 addresses obtained from
- * network cards
- */
- node_id[0] |= 0x01;
- }
- has_init = 1;
- }
- ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
- uu.clock_seq |= 0x8000;
- uu.time_mid = (uint16_t)clock_mid;
- uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
- memcpy(uu.node, node_id, 6);
- uuid_pack(&uu, out);
- return ret;
-}
-
-/*
- * Generate time-based UUID and store it to @out
- *
- * Tries to guarantee uniqueness of the generated UUIDs by obtaining them from the uuidd daemon,
- * or, if uuidd is not usable, by using the global clock state counter (see get_clock()).
- * If neither of these is possible (e.g. because of insufficient permissions), it generates
- * the UUID anyway, but returns -1. Otherwise, returns 0.
- */
-static int uuid_generate_time_generic(uuid_t out)
-{
-#ifdef HAVE_TLS
- THREAD_LOCAL int num = 0;
- THREAD_LOCAL struct uuid uu;
- THREAD_LOCAL time_t last_time = 0;
- time_t now;
-
- if (num > 0) {
- now = time(0);
- if (now > last_time + 1)
- num = 0;
- }
- if (num <= 0) {
- num = 1000;
- if (get_uuid_via_daemon(UUIDD_OP_BULK_TIME_UUID,
- out, &num) == 0) {
- last_time = time(0);
- uuid_unpack(out, &uu);
- num--;
- return 0;
- }
- num = 0;
- }
- if (num > 0) {
- uu.time_low++;
- if (uu.time_low == 0) {
- uu.time_mid++;
- if (uu.time_mid == 0)
- uu.time_hi_and_version++;
- }
- num--;
- uuid_pack(&uu, out);
- return 0;
- }
-#else
- if (get_uuid_via_daemon(UUIDD_OP_TIME_UUID, out, 0) == 0)
- return 0;
-#endif
-
- return __uuid_generate_time(out, 0);
-}
-
-/*
- * Generate time-based UUID and store it to @out.
- *
- * Discards return value from uuid_generate_time_generic()
- */
-void uuid_generate_time(uuid_t out)
-{
- (void)uuid_generate_time_generic(out);
-}
-
-int uuid_generate_time_safe(uuid_t out)
-{
- return uuid_generate_time_generic(out);
-}
-
-void __uuid_generate_random(uuid_t out, int *num)
-{
- uuid_t buf;
- struct uuid uu;
- int i, n;
-
- if (!num || !*num)
- n = 1;
- else
- n = *num;
-
- for (i = 0; i < n; i++) {
- random_get_bytes(buf, sizeof(buf));
- uuid_unpack(buf, &uu);
-
- uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
- uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
- uuid_pack(&uu, out);
- out += sizeof(uuid_t);
- }
-}
-
-void uuid_generate_random(uuid_t out)
-{
- int num = 1;
- /* No real reason to use the daemon for random uuid's -- yet */
-
- __uuid_generate_random(out, &num);
-}
-
-/*
- * Check whether good random source (/dev/random or /dev/urandom)
- * is available.
- */
-static int have_random_source(void)
-{
- struct stat s;
-
- return (!stat("/dev/random", &s) || !stat("/dev/urandom", &s));
-}
-
-/*
- * This is the generic front-end to uuid_generate_random and
- * uuid_generate_time. It uses uuid_generate_random only if
- * /dev/urandom is available, since otherwise we won't have
- * high-quality randomness.
- */
-void uuid_generate(uuid_t out)
-{
- if (have_random_source())
- uuid_generate_random(out);
- else
- uuid_generate_time(out);
-}
-
-/*
- * General purpose random utilities
- *
- * Based on libuuid code.
- *
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- */
-#include
-#include
-#include
-#include
-#include
-#include
-
-#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
-#include
-#endif
-
-// #include "randutils.h"
-
-#ifdef HAVE_TLS
-#define THREAD_LOCAL static __thread
-#else
-#define THREAD_LOCAL static
-#endif
-
-#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
-#define DO_JRAND_MIX
-THREAD_LOCAL unsigned short ul_jrand_seed[3];
-#endif
-
-int random_get_fd(void)
-{
- int i, fd;
- struct timeval tv;
-
- gettimeofday(&tv, 0);
- fd = open("/dev/urandom", O_RDONLY);
- if (fd == -1)
- fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
- if (fd >= 0) {
- i = fcntl(fd, F_GETFD);
- if (i >= 0)
- fcntl(fd, F_SETFD, i | FD_CLOEXEC);
- }
- srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
-
-#ifdef DO_JRAND_MIX
- ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
- ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
- ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
-#endif
- /* Crank the random number generator a few times */
- gettimeofday(&tv, 0);
- for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
- rand();
- return fd;
-}
-
-/*
- * Generate a stream of random nbytes into buf.
- * Use /dev/urandom if possible, and if not,
- * use glibc pseudo-random functions.
- */
-void random_get_bytes(void *buf, size_t nbytes)
-{
- size_t i, n = nbytes;
- int fd = random_get_fd();
- int lose_counter = 0;
- unsigned char *cp = (unsigned char *)buf;
-
- if (fd >= 0) {
- while (n > 0) {
- ssize_t x = read(fd, cp, n);
- if (x <= 0) {
- if (lose_counter++ > 16)
- break;
- continue;
- }
- n -= x;
- cp += x;
- lose_counter = 0;
- }
-
- close(fd);
- }
-
- /*
- * We do this all the time, but this is the only source of
- * randomness if /dev/random/urandom is out to lunch.
- */
- for (cp = (unsigned char *)buf, i = 0; i < nbytes; i++)
- *cp++ ^= (rand() >> 7) & 0xFF;
-
-#ifdef DO_JRAND_MIX
- {
- unsigned short tmp_seed[3];
-
- memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
- ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
- for (cp = buf, i = 0; i < nbytes; i++)
- *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
- memcpy(ul_jrand_seed, tmp_seed,
- sizeof(ul_jrand_seed) - sizeof(unsigned short));
- }
-#endif
-
- return;
-}
-
-#ifdef TEST_PROGRAM
-int main(int argc __attribute__((__unused__)),
- char *argv[] __attribute__((__unused__)))
-{
- unsigned int v, i;
-
- /* generate and print 10 random numbers */
- for (i = 0; i < 10; i++) {
- random_get_bytes(&v, sizeof(v));
- printf("%d\n", v);
- }
-
- return EXIT_SUCCESS;
-}
-#endif /* TEST_PROGRAM */
-
-/*
- * Internal routine for packing UUIDs
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include
-// #include "uuidP.h"
-
-void uuid_pack(const struct uuid *uu, uuid_t ptr)
-{
- uint32_t tmp;
- unsigned char *out = ptr;
-
- tmp = uu->time_low;
- out[3] = (unsigned char)tmp;
- tmp >>= 8;
- out[2] = (unsigned char)tmp;
- tmp >>= 8;
- out[1] = (unsigned char)tmp;
- tmp >>= 8;
- out[0] = (unsigned char)tmp;
-
- tmp = uu->time_mid;
- out[5] = (unsigned char)tmp;
- tmp >>= 8;
- out[4] = (unsigned char)tmp;
-
- tmp = uu->time_hi_and_version;
- out[7] = (unsigned char)tmp;
- tmp >>= 8;
- out[6] = (unsigned char)tmp;
-
- tmp = uu->clock_seq;
- out[9] = (unsigned char)tmp;
- tmp >>= 8;
- out[8] = (unsigned char)tmp;
-
- memcpy(out + 10, uu->node, 6);
-}
-
-/*
- * Internal routine for unpacking UUID
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include
-// #include "uuidP.h"
-
-void uuid_unpack(const uuid_t in, struct uuid *uu)
-{
- const uint8_t *ptr = in;
- uint32_t tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- tmp = (tmp << 8) | *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->time_low = tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->time_mid = tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->time_hi_and_version = tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->clock_seq = tmp;
-
- memcpy(uu->node, ptr, 6);
-}
diff --git a/trunk/src/kernel/srs_kernel_uuid.hpp b/trunk/src/kernel/srs_kernel_uuid.hpp
deleted file mode 100644
index 2f3af2e09ff..00000000000
--- a/trunk/src/kernel/srs_kernel_uuid.hpp
+++ /dev/null
@@ -1,111 +0,0 @@
-//
-// libuuid BSD License @see https://sourceforge.net/projects/libuuid/
-//
-// SPDX-License-Identifier: BSD-3-Clause
-//
-#include
-
-/*
- * Public include file for the UUID library
- *
- * Copyright (C) 1996, 1997, 1998 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#ifndef _UUID_UUID_H
-#define _UUID_UUID_H
-
-#include
-#ifndef _WIN32
-#include
-#endif
-#include
-
-typedef unsigned char uuid_t[16];
-
-/* UUID Variant definitions */
-#define UUID_VARIANT_NCS 0
-#define UUID_VARIANT_DCE 1
-#define UUID_VARIANT_MICROSOFT 2
-#define UUID_VARIANT_OTHER 3
-
-/* UUID Type definitions */
-#define UUID_TYPE_DCE_TIME 1
-#define UUID_TYPE_DCE_RANDOM 4
-
-/* Allow UUID constants to be defined */
-#ifdef __GNUC__
-#define UUID_DEFINE(name, u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11, u12, u13, u14, u15) \
- static const uuid_t name __attribute__((unused)) = {u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11, u12, u13, u14, u15}
-#else
-#define UUID_DEFINE(name, u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11, u12, u13, u14, u15) \
- static const uuid_t name = {u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11, u12, u13, u14, u15}
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* clear.c */
-void uuid_clear(uuid_t uu);
-
-/* compare.c */
-int uuid_compare(const uuid_t uu1, const uuid_t uu2);
-
-/* copy.c */
-void uuid_copy(uuid_t dst, const uuid_t src);
-
-/* gen_uuid.c */
-void uuid_generate(uuid_t out);
-void uuid_generate_random(uuid_t out);
-void uuid_generate_time(uuid_t out);
-int uuid_generate_time_safe(uuid_t out);
-
-/* isnull.c */
-int uuid_is_null(const uuid_t uu);
-
-/* parse.c */
-int uuid_parse(const char *in, uuid_t uu);
-
-/* unparse.c */
-void uuid_unparse(const uuid_t uu, char *out);
-void uuid_unparse_lower(const uuid_t uu, char *out);
-void uuid_unparse_upper(const uuid_t uu, char *out);
-
-/* uuid_time.c */
-time_t uuid_time(const uuid_t uu, struct timeval *ret_tv);
-int uuid_type(const uuid_t uu);
-int uuid_variant(const uuid_t uu);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _UUID_UUID_H */
diff --git a/trunk/src/protocol/srs_protocol_utility.cpp b/trunk/src/protocol/srs_protocol_utility.cpp
index e6d2f37229d..bc67ad1491f 100644
--- a/trunk/src/protocol/srs_protocol_utility.cpp
+++ b/trunk/src/protocol/srs_protocol_utility.cpp
@@ -8,10 +8,10 @@
#include
+#include
#include
#include
#include
-#include
using namespace std;
#include
diff --git a/trunk/src/utest/srs_utest_kernel3.cpp b/trunk/src/utest/srs_utest_kernel3.cpp
new file mode 100644
index 00000000000..53166b5e70a
--- /dev/null
+++ b/trunk/src/utest/srs_utest_kernel3.cpp
@@ -0,0 +1,810 @@
+//
+// Copyright (c) 2013-2025 The SRS Authors
+//
+// SPDX-License-Identifier: MIT
+//
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+// Mock classes for IO testing
+class MockSrsReader : public ISrsReader
+{
+public:
+ std::string data_;
+ size_t pos_;
+ srs_error_t read_error_;
+
+public:
+ MockSrsReader(const std::string &data) : data_(data), pos_(0), read_error_(srs_success) {}
+ virtual ~MockSrsReader() {}
+
+public:
+ virtual srs_error_t read(void *buf, size_t size, ssize_t *nread)
+ {
+ if (read_error_ != srs_success) {
+ return srs_error_copy(read_error_);
+ }
+
+ size_t available = data_.size() - pos_;
+ size_t to_read = std::min(size, available);
+
+ if (to_read > 0) {
+ memcpy(buf, data_.data() + pos_, to_read);
+ pos_ += to_read;
+ }
+
+ if (nread)
+ *nread = to_read;
+ return srs_success;
+ }
+
+ void set_error(srs_error_t err) { read_error_ = err; }
+};
+
+class MockSrsWriter : public ISrsWriter
+{
+public:
+ std::string written_data_;
+ srs_error_t write_error_;
+
+public:
+ MockSrsWriter() : write_error_(srs_success) {}
+ virtual ~MockSrsWriter() {}
+
+public:
+ virtual srs_error_t write(void *buf, size_t size, ssize_t *nwrite)
+ {
+ if (write_error_ != srs_success) {
+ return srs_error_copy(write_error_);
+ }
+
+ written_data_.append((char *)buf, size);
+ if (nwrite)
+ *nwrite = size;
+ return srs_success;
+ }
+
+ virtual srs_error_t writev(const iovec *iov, int iov_size, ssize_t *nwrite)
+ {
+ if (write_error_ != srs_success) {
+ return srs_error_copy(write_error_);
+ }
+
+ ssize_t total = 0;
+ for (int i = 0; i < iov_size; i++) {
+ written_data_.append((char *)iov[i].iov_base, iov[i].iov_len);
+ total += iov[i].iov_len;
+ }
+
+ if (nwrite)
+ *nwrite = total;
+ return srs_success;
+ }
+
+ void set_error(srs_error_t err) { write_error_ = err; }
+};
+
+class MockSrsSeeker : public ISrsSeeker
+{
+public:
+ off_t position_;
+ srs_error_t seek_error_;
+
+public:
+ MockSrsSeeker() : position_(0), seek_error_(srs_success) {}
+ virtual ~MockSrsSeeker() {}
+
+public:
+ virtual srs_error_t lseek(off_t offset, int whence, off_t *seeked)
+ {
+ if (seek_error_ != srs_success) {
+ return srs_error_copy(seek_error_);
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ position_ = offset;
+ break;
+ case SEEK_CUR:
+ position_ += offset;
+ break;
+ case SEEK_END:
+ position_ = 1000 + offset; // Mock file size of 1000
+ break;
+ }
+
+ if (seeked)
+ *seeked = position_;
+ return srs_success;
+ }
+
+ void set_error(srs_error_t err) { seek_error_ = err; }
+};
+
+// Tests for srs_kernel_io.hpp
+VOID TEST(KernelIOTest, ISrsReaderInterface)
+{
+ MockSrsReader reader("Hello World");
+
+ char buf[20];
+ ssize_t nread = 0;
+
+ // Test successful read
+ srs_error_t err = reader.read(buf, 5, &nread);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(5, (int)nread);
+ EXPECT_EQ(0, memcmp(buf, "Hello", 5));
+
+ // Test reading remaining data
+ err = reader.read(buf, 10, &nread);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(6, (int)nread);
+ EXPECT_EQ(0, memcmp(buf, " World", 6));
+
+ // Test reading beyond end
+ err = reader.read(buf, 10, &nread);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(0, (int)nread);
+}
+
+VOID TEST(KernelIOTest, ISrsReaderErrorHandling)
+{
+ srs_error_t err;
+
+ MockSrsReader reader("test");
+ reader.set_error(srs_error_new(ERROR_SYSTEM_IO_INVALID, "mock error"));
+
+ char buf[10];
+ ssize_t nread = 0;
+
+ err = reader.read(buf, 5, &nread);
+ EXPECT_TRUE(err != srs_success);
+ srs_freep(err);
+}
+
+VOID TEST(KernelIOTest, ISrsWriterInterface)
+{
+ MockSrsWriter writer;
+
+ const char *data1 = "Hello";
+ const char *data2 = " World";
+ ssize_t nwrite = 0;
+
+ // Test write
+ srs_error_t err = writer.write((void *)data1, 5, &nwrite);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(5, (int)nwrite);
+ EXPECT_EQ("Hello", writer.written_data_);
+
+ // Test another write
+ err = writer.write((void *)data2, 6, &nwrite);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(6, (int)nwrite);
+ EXPECT_EQ("Hello World", writer.written_data_);
+}
+
+VOID TEST(KernelIOTest, ISrsVectorWriterInterface)
+{
+ MockSrsWriter writer;
+
+ const char *data1 = "Hello";
+ const char *data2 = " ";
+ const char *data3 = "World";
+
+ iovec iov[3];
+ iov[0].iov_base = (void *)data1;
+ iov[0].iov_len = 5;
+ iov[1].iov_base = (void *)data2;
+ iov[1].iov_len = 1;
+ iov[2].iov_base = (void *)data3;
+ iov[2].iov_len = 5;
+
+ ssize_t nwrite = 0;
+ srs_error_t err = writer.writev(iov, 3, &nwrite);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(11, (int)nwrite);
+ EXPECT_EQ("Hello World", writer.written_data_);
+}
+
+VOID TEST(KernelIOTest, ISrsSeekerInterface)
+{
+ MockSrsSeeker seeker;
+
+ off_t seeked = 0;
+
+ // Test SEEK_SET
+ srs_error_t err = seeker.lseek(100, SEEK_SET, &seeked);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(100, (int)seeked);
+
+ // Test SEEK_CUR
+ err = seeker.lseek(50, SEEK_CUR, &seeked);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(150, (int)seeked);
+
+ // Test SEEK_END
+ err = seeker.lseek(-10, SEEK_END, &seeked);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(990, (int)seeked); // 1000 - 10
+}
+
+// Tests for srs_kernel_packet.hpp
+VOID TEST(KernelPacketTest, SrsNaluSampleBasic)
+{
+ // Test default constructor
+ SrsNaluSample sample1;
+ EXPECT_EQ(0, sample1.size_);
+ EXPECT_TRUE(sample1.bytes_ == NULL);
+
+ // Test constructor with data
+ char data[] = {0x00, 0x00, 0x00, 0x01, 0x67};
+ SrsNaluSample sample2(data, sizeof(data));
+ EXPECT_EQ(sizeof(data), (size_t)sample2.size_);
+ EXPECT_TRUE(sample2.bytes_ == data);
+}
+
+VOID TEST(KernelPacketTest, SrsNaluSampleCopy)
+{
+ char data[] = {0x00, 0x00, 0x00, 0x01, 0x67};
+ SrsNaluSample original(data, sizeof(data));
+
+ SrsNaluSample *copy = original.copy();
+ EXPECT_TRUE(copy != NULL);
+ EXPECT_EQ(original.size_, copy->size_);
+ EXPECT_TRUE(copy->bytes_ == original.bytes_); // Should share the same pointer
+
+ srs_freep(copy);
+}
+
+VOID TEST(KernelPacketTest, SrsMediaPacketBasic)
+{
+ SrsMediaPacket packet;
+
+ // Test default values
+ EXPECT_EQ(0, packet.timestamp_);
+ EXPECT_EQ(SrsFrameTypeReserved, packet.message_type_);
+ EXPECT_EQ(0, packet.stream_id_);
+ EXPECT_TRUE(packet.payload() == NULL);
+ EXPECT_EQ(0, packet.size());
+
+ // Test type checking
+ EXPECT_FALSE(packet.is_av());
+ EXPECT_FALSE(packet.is_audio());
+ EXPECT_FALSE(packet.is_video());
+}
+
+VOID TEST(KernelPacketTest, SrsMediaPacketWrap)
+{
+ SrsMediaPacket packet;
+
+ // Use heap-allocated memory for wrap() test
+ int data_size = 9;
+ char *data = new char[data_size];
+ memcpy(data, "test data", data_size);
+
+ packet.wrap(data, data_size);
+
+ EXPECT_TRUE(packet.payload() != NULL);
+ EXPECT_EQ(data_size, packet.size());
+ EXPECT_EQ(0, memcmp(packet.payload(), "test data", data_size));
+
+ // Note: packet destructor will free the data
+}
+
+VOID TEST(KernelPacketTest, SrsMediaPacketTypeChecking)
+{
+ SrsMediaPacket packet;
+
+ // Test audio packet
+ packet.message_type_ = SrsFrameTypeAudio;
+ EXPECT_TRUE(packet.is_av());
+ EXPECT_TRUE(packet.is_audio());
+ EXPECT_FALSE(packet.is_video());
+
+ // Test video packet
+ packet.message_type_ = SrsFrameTypeVideo;
+ EXPECT_TRUE(packet.is_av());
+ EXPECT_FALSE(packet.is_audio());
+ EXPECT_TRUE(packet.is_video());
+
+ // Test script packet
+ packet.message_type_ = SrsFrameTypeScript;
+ EXPECT_FALSE(packet.is_av());
+ EXPECT_FALSE(packet.is_audio());
+ EXPECT_FALSE(packet.is_video());
+}
+
+// Mock classes for resource testing
+class MockSrsResource : public ISrsResource
+{
+public:
+ SrsContextId cid_;
+ std::string desc_;
+
+public:
+ MockSrsResource()
+ {
+ desc_ = "mock resource";
+ }
+ virtual ~MockSrsResource() {}
+
+public:
+ virtual const SrsContextId &get_id() { return cid_; }
+ virtual std::string desc() { return desc_; }
+
+ void set_id(const SrsContextId &cid) { cid_ = cid; }
+ void set_desc(const std::string &desc) { desc_ = desc; }
+};
+
+class MockSrsDisposingHandler : public ISrsDisposingHandler
+{
+public:
+ std::vector before_dispose_calls_;
+ std::vector disposing_calls_;
+
+public:
+ MockSrsDisposingHandler() {}
+ virtual ~MockSrsDisposingHandler() {}
+
+public:
+ virtual void on_before_dispose(ISrsResource *c)
+ {
+ before_dispose_calls_.push_back(c);
+ }
+ virtual void on_disposing(ISrsResource *c)
+ {
+ disposing_calls_.push_back(c);
+ }
+};
+
+// Tests for srs_kernel_resource.hpp
+VOID TEST(KernelResourceTest, ISrsResourceInterface)
+{
+ MockSrsResource resource;
+
+ // Test default description
+ EXPECT_EQ("mock resource", resource.desc());
+
+ // Test setting description
+ resource.set_desc("test resource");
+ EXPECT_EQ("test resource", resource.desc());
+
+ // Test context ID
+ SrsContextId cid;
+ resource.set_id(cid);
+ EXPECT_STREQ(cid.c_str(), resource.get_id().c_str());
+}
+
+VOID TEST(KernelResourceTest, SrsSharedResourceBasic)
+{
+ MockSrsResource *raw_resource = new MockSrsResource();
+ raw_resource->set_desc("shared test");
+
+ SrsSharedResource shared_resource(raw_resource);
+
+ // Test access through shared resource
+ EXPECT_TRUE(shared_resource.get() != NULL);
+ EXPECT_EQ("shared test", shared_resource->desc());
+ EXPECT_EQ("shared test", shared_resource.desc());
+
+ // Test copy constructor
+ SrsSharedResource copy_resource(shared_resource);
+ EXPECT_TRUE(copy_resource.get() != NULL);
+ EXPECT_EQ("shared test", copy_resource->desc());
+
+ // Test assignment operator
+ SrsSharedResource assigned_resource;
+ assigned_resource = shared_resource;
+ EXPECT_TRUE(assigned_resource.get() != NULL);
+ EXPECT_EQ("shared test", assigned_resource->desc());
+}
+
+// Mock classes for hourglass testing
+class MockSrsHourGlass : public ISrsHourGlass
+{
+public:
+ std::vector events_;
+ std::vector intervals_;
+ std::vector ticks_;
+
+public:
+ MockSrsHourGlass() {}
+ virtual ~MockSrsHourGlass() {}
+
+public:
+ virtual srs_error_t notify(int event, srs_utime_t interval, srs_utime_t tick)
+ {
+ events_.push_back(event);
+ intervals_.push_back(interval);
+ ticks_.push_back(tick);
+ return srs_success;
+ }
+
+ void clear()
+ {
+ events_.clear();
+ intervals_.clear();
+ ticks_.clear();
+ }
+};
+
+class MockSrsFastTimer : public ISrsFastTimer
+{
+public:
+ std::vector timer_calls_;
+
+public:
+ MockSrsFastTimer() {}
+ virtual ~MockSrsFastTimer() {}
+
+public:
+ virtual srs_error_t on_timer(srs_utime_t interval)
+ {
+ timer_calls_.push_back(interval);
+ return srs_success;
+ }
+
+ void clear()
+ {
+ timer_calls_.clear();
+ }
+};
+
+// Tests for srs_kernel_hourglass.hpp
+VOID TEST(KernelHourglassTest, ISrsHourGlassInterface)
+{
+ MockSrsHourGlass handler;
+
+ // Test notify
+ srs_error_t err = handler.notify(1, 1000, 5000);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(1, (int)handler.events_.size());
+ EXPECT_EQ(1, handler.events_[0]);
+ EXPECT_EQ(1000, (int)handler.intervals_[0]);
+ EXPECT_EQ(5000, (int)handler.ticks_[0]);
+
+ // Test multiple notifications
+ err = handler.notify(2, 2000, 10000);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(2, (int)handler.events_.size());
+ EXPECT_EQ(2, handler.events_[1]);
+ EXPECT_EQ(2000, (int)handler.intervals_[1]);
+ EXPECT_EQ(10000, (int)handler.ticks_[1]);
+}
+
+VOID TEST(KernelHourglassTest, ISrsFastTimerInterface)
+{
+ MockSrsFastTimer timer;
+
+ // Test timer callback
+ srs_error_t err = timer.on_timer(100 * SRS_UTIME_MILLISECONDS);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(1, (int)timer.timer_calls_.size());
+ EXPECT_EQ(100 * SRS_UTIME_MILLISECONDS, timer.timer_calls_[0]);
+
+ // Test multiple timer calls
+ err = timer.on_timer(200 * SRS_UTIME_MILLISECONDS);
+ EXPECT_EQ(srs_success, err);
+ EXPECT_EQ(2, (int)timer.timer_calls_.size());
+ EXPECT_EQ(200 * SRS_UTIME_MILLISECONDS, timer.timer_calls_[1]);
+}
+
+// Tests for srs_kernel_consts.hpp
+VOID TEST(KernelConstsTest, RTMPConstants)
+{
+ // Test RTMP default values
+ EXPECT_EQ(1935, SRS_CONSTS_RTMP_DEFAULT_PORT);
+ EXPECT_STREQ("__defaultVhost__", SRS_CONSTS_RTMP_DEFAULT_VHOST);
+ EXPECT_STREQ("__defaultApp__", SRS_CONSTS_RTMP_DEFAULT_APP);
+
+ // Test chunk sizes
+ EXPECT_EQ(60000, SRS_CONSTS_RTMP_SRS_CHUNK_SIZE);
+ EXPECT_EQ(128, SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE);
+
+ // Verify chunk size constraints
+ EXPECT_GE(SRS_CONSTS_RTMP_SRS_CHUNK_SIZE, SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE);
+ EXPECT_LE(SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE, 65536);
+ EXPECT_GE(SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE, 128);
+}
+
+VOID TEST(KernelConstsTest, HTTPConstants)
+{
+ // Test HTTP default ports
+ EXPECT_EQ(80, SRS_DEFAULT_HTTP_PORT);
+ EXPECT_EQ(443, SRS_DEFAULT_HTTPS_PORT);
+ EXPECT_EQ(6379, SRS_DEFAULT_REDIS_PORT);
+
+ // Verify port ranges are valid
+ EXPECT_GT(SRS_DEFAULT_HTTP_PORT, 0);
+ EXPECT_LT(SRS_DEFAULT_HTTP_PORT, 65536);
+ EXPECT_GT(SRS_DEFAULT_HTTPS_PORT, 0);
+ EXPECT_LT(SRS_DEFAULT_HTTPS_PORT, 65536);
+}
+
+// Tests for srs_kernel_utility.hpp
+VOID TEST(KernelUtilityTest, StringFormatting)
+{
+ // Test integer formatting
+ EXPECT_EQ("123", srs_strconv_format_int(123));
+ EXPECT_EQ("-456", srs_strconv_format_int(-456));
+ EXPECT_EQ("0", srs_strconv_format_int(0));
+
+ // Test float formatting
+ std::string float_result = srs_strconv_format_float(3.14159);
+ EXPECT_TRUE(float_result.find("3.14") != std::string::npos);
+
+ // Test bool formatting
+ EXPECT_EQ("on", srs_strconv_format_bool(true));
+ EXPECT_EQ("off", srs_strconv_format_bool(false));
+}
+
+VOID TEST(KernelUtilityTest, StringManipulation)
+{
+ // Test string replacement
+ EXPECT_EQ("hello world", srs_strings_replace("hello test", "test", "world"));
+ EXPECT_EQ("abc abc abc", srs_strings_replace("def def def", "def", "abc"));
+
+ // Test string trimming
+ EXPECT_EQ("hello", srs_strings_trim_end("hello ", " "));
+ EXPECT_EQ("world", srs_strings_trim_start(" world", " "));
+
+ // Test string removal
+ EXPECT_EQ("helloworld", srs_strings_remove("hello world", " "));
+ EXPECT_EQ("abc", srs_strings_remove("a,b,c", ","));
+}
+
+VOID TEST(KernelUtilityTest, StringChecking)
+{
+ // Test ends with
+ EXPECT_TRUE(srs_strings_ends_with("test.mp4", ".mp4"));
+ EXPECT_FALSE(srs_strings_ends_with("test.flv", ".mp4"));
+ EXPECT_TRUE(srs_strings_ends_with("file.log", ".log", ".txt"));
+
+ // Test starts with
+ EXPECT_TRUE(srs_strings_starts_with("rtmp://", "rtmp://"));
+ EXPECT_FALSE(srs_strings_starts_with("http://", "rtmp://"));
+ EXPECT_TRUE(srs_strings_starts_with("http://", "http://", "https://"));
+
+ // Test contains
+ EXPECT_TRUE(srs_strings_contains("hello world", "world"));
+ EXPECT_FALSE(srs_strings_contains("hello world", "test"));
+ EXPECT_TRUE(srs_strings_contains("test string", "test", "string"));
+}
+
+VOID TEST(KernelUtilityTest, StringSplitting)
+{
+ // Test basic splitting
+ std::vector result = srs_strings_split("a,b,c", ",");
+ EXPECT_EQ(3, (int)result.size());
+ EXPECT_EQ("a", result[0]);
+ EXPECT_EQ("b", result[1]);
+ EXPECT_EQ("c", result[2]);
+
+ // Test empty string splitting
+ std::vector empty_result = srs_strings_split("", ",");
+ EXPECT_EQ(1, (int)empty_result.size());
+ EXPECT_EQ("", empty_result[0]);
+
+ // Test no separator found
+ std::vector no_sep = srs_strings_split("hello", ",");
+ EXPECT_EQ(1, (int)no_sep.size());
+ EXPECT_EQ("hello", no_sep[0]);
+}
+
+VOID TEST(KernelUtilityTest, HexDumping)
+{
+ // Test hex dumping
+ std::string hex_result = srs_strings_dumps_hex("ABC", 3);
+ EXPECT_TRUE(hex_result.find("41") != std::string::npos); // 'A' = 0x41
+ EXPECT_TRUE(hex_result.find("42") != std::string::npos); // 'B' = 0x42
+ EXPECT_TRUE(hex_result.find("43") != std::string::npos); // 'C' = 0x43
+
+ // Test empty string hex dump
+ std::string empty_hex = srs_strings_dumps_hex("", 0);
+ EXPECT_TRUE(empty_hex.empty() || empty_hex == "");
+}
+
+VOID TEST(KernelUtilityTest, SystemProperties)
+{
+ // Test endianness detection
+ bool is_little = srs_is_little_endian();
+ // Just verify it returns a consistent value
+ EXPECT_EQ(is_little, srs_is_little_endian());
+}
+
+// Tests for srs_kernel_stream.hpp
+VOID TEST(KernelStreamTest, SimpleStreamBasics)
+{
+ SrsSimpleStream stream;
+
+ // Test initial state
+ EXPECT_EQ(0, stream.length());
+ EXPECT_TRUE(stream.bytes() == NULL);
+
+ // Test appending data
+ std::string test_data = "hello";
+ stream.append(test_data.c_str(), test_data.length());
+ EXPECT_EQ(5, stream.length());
+ EXPECT_TRUE(stream.bytes() != NULL);
+
+ // Test erasing data
+ stream.erase(2);
+ EXPECT_EQ(3, stream.length());
+}
+
+VOID TEST(KernelStreamTest, SimpleStreamOperations)
+{
+ SrsSimpleStream stream;
+
+ // Add some test data
+ stream.append("test data", 9);
+
+ // Test data access
+ EXPECT_TRUE(stream.bytes() != NULL);
+ EXPECT_EQ('t', stream.bytes()[0]);
+ EXPECT_EQ('e', stream.bytes()[1]);
+
+ // Test erasing all data
+ stream.erase(stream.length());
+ EXPECT_EQ(0, stream.length());
+ EXPECT_TRUE(stream.bytes() == NULL);
+}
+
+VOID TEST(KernelStreamTest, SimpleStreamAppending)
+{
+ SrsSimpleStream stream1;
+ SrsSimpleStream stream2;
+
+ // Add data to both streams
+ stream1.append("hello", 5);
+ stream2.append(" world", 6);
+
+ // Test appending one stream to another
+ stream1.append(&stream2);
+ EXPECT_EQ(11, stream1.length());
+
+ // Verify the combined content
+ char *data = stream1.bytes();
+ EXPECT_TRUE(data != NULL);
+ EXPECT_EQ('h', data[0]);
+ EXPECT_EQ(' ', data[5]);
+ EXPECT_EQ('w', data[6]);
+}
+
+// Tests for srs_kernel_pithy_print.hpp
+VOID TEST(KernelPithyPrintTest, AlonePithyPrint)
+{
+ SrsAlonePithyPrint print;
+
+ // The behavior depends on internal timing, just verify it doesn't crash
+ bool can_print_initial = print.can_print();
+ EXPECT_TRUE(can_print_initial == true || can_print_initial == false);
+
+ // After elapse, timing should be updated
+ print.elapse();
+ // The behavior depends on internal timing, just verify it doesn't crash
+ bool can_print = print.can_print();
+ EXPECT_TRUE(can_print == true || can_print == false);
+}
+
+VOID TEST(KernelPithyPrintTest, ErrorPithyPrint)
+{
+ SrsErrorPithyPrint error_print;
+
+ // Test with different error codes
+ srs_error_t err1 = srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "test error 1");
+ srs_error_t err2 = srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "test error 2");
+
+ // First call should be able to print
+ uint32_t nn = 0;
+ bool can_print1 = error_print.can_print(err1, &nn);
+ EXPECT_TRUE(can_print1 == true || can_print1 == false);
+
+ // Test with integer error codes
+ bool can_print_int = error_print.can_print(1001, &nn);
+ EXPECT_TRUE(can_print_int == true || can_print_int == false);
+
+ srs_freep(err1);
+ srs_freep(err2);
+}
+
+VOID TEST(KernelPithyPrintTest, PithyPrintFactories)
+{
+ // Test factory methods don't crash
+ SrsUniquePtr rtmp_play(SrsPithyPrint::create_rtmp_play());
+ EXPECT_TRUE(rtmp_play.get() != NULL);
+
+ SrsUniquePtr rtmp_publish(SrsPithyPrint::create_rtmp_publish());
+ EXPECT_TRUE(rtmp_publish.get() != NULL);
+
+ SrsUniquePtr hls(SrsPithyPrint::create_hls());
+ EXPECT_TRUE(hls.get() != NULL);
+
+ SrsUniquePtr rtc_play(SrsPithyPrint::create_rtc_play());
+ EXPECT_TRUE(rtc_play.get() != NULL);
+
+ // Test basic operations
+ rtmp_play->elapse();
+ bool can_print = rtmp_play->can_print();
+ EXPECT_TRUE(can_print == true || can_print == false);
+
+ srs_utime_t age = rtmp_play->age();
+ EXPECT_GE(age, 0);
+}
+
+// Tests for srs_kernel_rtc_queue.hpp
+VOID TEST(KernelRTCQueueTest, RtpRingBufferBasics)
+{
+ SrsRtpRingBuffer buffer(100);
+
+ // Test initial state
+ EXPECT_TRUE(buffer.empty());
+ EXPECT_EQ(0, buffer.size());
+
+ // Test basic operations without actual packets (just interface testing)
+ uint16_t nack_first = 0, nack_last = 0;
+ bool result = buffer.update(100, nack_first, nack_last);
+ EXPECT_TRUE(result == true || result == false); // Just verify it doesn't crash
+
+ // Test sequence operations
+ uint32_t extended_seq = buffer.get_extended_highest_sequence();
+ EXPECT_GE(extended_seq, 0);
+}
+
+VOID TEST(KernelRTCQueueTest, RtpRingBufferAdvance)
+{
+ SrsRtpRingBuffer buffer(50);
+
+ // Test advance operations
+ buffer.advance_to(10);
+ buffer.advance_to(20);
+
+ // Test notification methods (should not crash)
+ buffer.notify_nack_list_full();
+ buffer.notify_drop_seq(15);
+
+ // Verify buffer state
+ EXPECT_TRUE(buffer.empty() || !buffer.empty()); // Just verify it doesn't crash
+}
+
+VOID TEST(KernelRTCQueueTest, RtpNackForReceiver)
+{
+ SrsRtpRingBuffer buffer(100);
+ SrsRtpNackForReceiver nack(&buffer, 50);
+
+ // Test basic nack operations
+ nack.insert(100, 105);
+ nack.remove(102);
+
+ // Test finding nack info
+ SrsRtpNackInfo *info = nack.find(103);
+ EXPECT_TRUE(info != NULL || info == NULL); // Just verify it doesn't crash
+
+ // Test queue size check
+ nack.check_queue_size();
+
+ // Test RTT update
+ nack.update_rtt(50);
+
+ // Test getting nack sequences
+ SrsRtcpNack seqs;
+ uint32_t timeout_nacks = 0;
+ nack.get_nack_seqs(seqs, timeout_nacks);
+ EXPECT_GE(timeout_nacks, 0);
+}
diff --git a/trunk/src/utest/srs_utest_kernel3.hpp b/trunk/src/utest/srs_utest_kernel3.hpp
new file mode 100644
index 00000000000..f3c1589a5df
--- /dev/null
+++ b/trunk/src/utest/srs_utest_kernel3.hpp
@@ -0,0 +1,17 @@
+//
+// Copyright (c) 2013-2025 The SRS Authors
+//
+// SPDX-License-Identifier: MIT
+//
+
+#ifndef SRS_UTEST_KERNEL3_HPP
+#define SRS_UTEST_KERNEL3_HPP
+
+/*
+#include
+*/
+#include
+
+#include
+
+#endif
diff --git a/trunk/src/utest/srs_utest_protocol3.cpp b/trunk/src/utest/srs_utest_protocol3.cpp
index 818080170a5..a0a22c5c29f 100644
--- a/trunk/src/utest/srs_utest_protocol3.cpp
+++ b/trunk/src/utest/srs_utest_protocol3.cpp
@@ -13,10 +13,20 @@ using namespace std;
#include
#include
#include
+#include
+#include
+#include
#include
+#include
+#include
+#include
#include
+#include
+#include
#include
#include
+#include
+#include
#include
extern bool srs_is_valid_jsonp_callback(std::string callback);
@@ -35,3 +45,521 @@ VOID TEST(ProtocolHttpTest, JsonpCallbackName)
EXPECT_FALSE(srs_is_valid_jsonp_callback("callback!"));
EXPECT_FALSE(srs_is_valid_jsonp_callback("callback;"));
}
+
+// Mock classes for testing protocol connections
+class MockConnection : public ISrsConnection
+{
+public:
+ std::string ip_;
+
+public:
+ MockConnection(std::string ip = "127.0.0.1") : ip_(ip) {}
+ virtual ~MockConnection() {}
+
+public:
+ virtual std::string remote_ip() { return ip_; }
+ virtual std::string desc() { return "MockConnection"; }
+ virtual const SrsContextId &get_id()
+ {
+ static SrsContextId id = SrsContextId();
+ return id;
+ }
+};
+
+class MockExpire : public ISrsExpire
+{
+public:
+ bool expired_;
+
+public:
+ MockExpire() : expired_(false) {}
+ virtual ~MockExpire() {}
+
+public:
+ virtual void expire() { expired_ = true; }
+};
+
+VOID TEST(ProtocolConnTest, ISrsConnectionInterface)
+{
+ MockConnection conn("192.168.1.100");
+ EXPECT_STREQ("192.168.1.100", conn.remote_ip().c_str());
+ EXPECT_STREQ("MockConnection", conn.desc().c_str());
+}
+
+VOID TEST(ProtocolConnTest, ISrsExpireInterface)
+{
+ MockExpire expire;
+ EXPECT_FALSE(expire.expired_);
+
+ expire.expire();
+ EXPECT_TRUE(expire.expired_);
+}
+
+VOID TEST(ProtocolIOTest, ISrsProtocolReaderInterface)
+{
+ // Test that interfaces are properly defined
+ // These are abstract classes, so we just verify they exist
+ ISrsProtocolReader *reader = NULL;
+ ISrsProtocolWriter *writer = NULL;
+ ISrsProtocolReadWriter *rw = NULL;
+
+ // Just verify the pointers can be assigned
+ EXPECT_TRUE(reader == NULL);
+ EXPECT_TRUE(writer == NULL);
+ EXPECT_TRUE(rw == NULL);
+}
+
+VOID TEST(ProtocolFormatTest, SrsRtmpFormatBasic)
+{
+ srs_error_t err = srs_success;
+
+ SrsRtmpFormat format;
+
+ // Test metadata handling
+ SrsOnMetaDataPacket *meta = new SrsOnMetaDataPacket();
+ SrsUniquePtr meta_uptr(meta);
+
+ HELPER_EXPECT_SUCCESS(format.on_metadata(meta));
+}
+
+VOID TEST(ProtocolFormatTest, SrsRtmpFormatAudioVideo)
+{
+ srs_error_t err = srs_success;
+
+ SrsRtmpFormat format;
+
+ // Test audio packet handling
+ if (true) {
+ SrsMediaPacket *audio = new SrsMediaPacket();
+ SrsUniquePtr audio_uptr(audio);
+
+ char *audio_data = new char[6];
+ audio_data[0] = 0xaf;
+ audio_data[1] = 0x01;
+ audio_data[2] = 0x00;
+ audio_data[3] = 0x01;
+ audio_data[4] = 0x02;
+ audio_data[5] = 0x03;
+ audio->wrap(audio_data, 6);
+ audio->timestamp_ = 1000;
+ audio->message_type_ = SrsFrameTypeAudio;
+
+ HELPER_EXPECT_SUCCESS(format.on_audio(audio));
+ }
+
+ // Test video packet handling with AVC sequence header
+ if (true) {
+ SrsMediaPacket *video = new SrsMediaPacket();
+ SrsUniquePtr video_uptr(video);
+
+ // Create a proper AVC sequence header: 0x17 (keyframe + AVC), 0x00 (AVC sequence header)
+ // followed by minimal AVC decoder configuration record
+ char *video_data = new char[20];
+ video_data[0] = 0x17; // keyframe + AVC
+ video_data[1] = 0x00; // AVC sequence header
+ video_data[2] = 0x00;
+ video_data[3] = 0x00;
+ video_data[4] = 0x00; // composition time
+ video_data[5] = 0x01; // configuration version
+ video_data[6] = 0x64; // profile
+ video_data[7] = 0x00; // profile compatibility
+ video_data[8] = 0x1f; // level
+ video_data[9] = 0xff; // NALU length size - 1
+ video_data[10] = 0xe1; // number of SPS
+ video_data[11] = 0x00;
+ video_data[12] = 0x07; // SPS length
+ video_data[13] = 0x67;
+ video_data[14] = 0x64;
+ video_data[15] = 0x00; // SPS data
+ video_data[16] = 0x1f;
+ video_data[17] = 0xac;
+ video_data[18] = 0xd9;
+ video_data[19] = 0x40;
+ video->wrap(video_data, 20);
+ video->timestamp_ = 2000;
+ video->message_type_ = SrsFrameTypeVideo;
+
+ // Video processing may fail due to complex AVC validation - just test that method exists
+ srs_error_t video_err = format.on_video(video);
+ srs_freep(video_err);
+ // Don't assert success since AVC decoder configuration validation is complex
+ }
+
+ // Test direct timestamp-based calls - these may fail due to codec validation
+ // but we just test that the methods exist and don't crash
+ char test_data[] = {0x01, 0x02, 0x03, 0x04};
+ srs_error_t audio_err = format.on_audio(3000, test_data, sizeof(test_data));
+ srs_error_t video_err = format.on_video(4000, test_data, sizeof(test_data));
+ srs_freep(audio_err);
+ srs_freep(video_err);
+ // Don't assert success since codec validation may fail with test data
+}
+
+VOID TEST(ProtocolJsonTest, SrsJsonAnyBasic)
+{
+ // Test string creation and conversion
+ if (true) {
+ SrsJsonAny *str = SrsJsonAny::str("hello world");
+ SrsUniquePtr str_uptr(str);
+
+ EXPECT_TRUE(str->is_string());
+ EXPECT_FALSE(str->is_boolean());
+ EXPECT_FALSE(str->is_integer());
+ EXPECT_FALSE(str->is_number());
+ EXPECT_FALSE(str->is_object());
+ EXPECT_FALSE(str->is_array());
+ EXPECT_FALSE(str->is_null());
+
+ EXPECT_STREQ("hello world", str->to_str().c_str());
+ }
+
+ // Test boolean creation and conversion
+ if (true) {
+ SrsJsonAny *boolean = SrsJsonAny::boolean(true);
+ SrsUniquePtr boolean_uptr(boolean);
+
+ EXPECT_FALSE(boolean->is_string());
+ EXPECT_TRUE(boolean->is_boolean());
+ EXPECT_FALSE(boolean->is_integer());
+ EXPECT_FALSE(boolean->is_number());
+ EXPECT_FALSE(boolean->is_object());
+ EXPECT_FALSE(boolean->is_array());
+ EXPECT_FALSE(boolean->is_null());
+
+ EXPECT_TRUE(boolean->to_boolean());
+ }
+
+ // Test integer creation and conversion
+ if (true) {
+ SrsJsonAny *integer = SrsJsonAny::integer(12345);
+ SrsUniquePtr integer_uptr(integer);
+
+ EXPECT_FALSE(integer->is_string());
+ EXPECT_FALSE(integer->is_boolean());
+ EXPECT_TRUE(integer->is_integer());
+ EXPECT_FALSE(integer->is_number());
+ EXPECT_FALSE(integer->is_object());
+ EXPECT_FALSE(integer->is_array());
+ EXPECT_FALSE(integer->is_null());
+
+ EXPECT_EQ(12345, integer->to_integer());
+ }
+
+ // Test number creation and conversion
+ if (true) {
+ SrsJsonAny *number = SrsJsonAny::number(3.14159);
+ SrsUniquePtr number_uptr(number);
+
+ EXPECT_FALSE(number->is_string());
+ EXPECT_FALSE(number->is_boolean());
+ EXPECT_FALSE(number->is_integer());
+ EXPECT_TRUE(number->is_number());
+ EXPECT_FALSE(number->is_object());
+ EXPECT_FALSE(number->is_array());
+ EXPECT_FALSE(number->is_null());
+
+ EXPECT_NEAR(3.14159, number->to_number(), 0.00001);
+ }
+
+ // Test null creation and conversion
+ if (true) {
+ SrsJsonAny *null_val = SrsJsonAny::null();
+ SrsUniquePtr null_uptr(null_val);
+
+ EXPECT_FALSE(null_val->is_string());
+ EXPECT_FALSE(null_val->is_boolean());
+ EXPECT_FALSE(null_val->is_integer());
+ EXPECT_FALSE(null_val->is_number());
+ EXPECT_FALSE(null_val->is_object());
+ EXPECT_FALSE(null_val->is_array());
+ EXPECT_TRUE(null_val->is_null());
+ }
+}
+
+VOID TEST(ProtocolJsonTest, SrsJsonObjectArray)
+{
+ // Test object creation
+ if (true) {
+ SrsJsonObject *obj = SrsJsonAny::object();
+ SrsUniquePtr obj_uptr(obj);
+
+ EXPECT_FALSE(obj->is_string());
+ EXPECT_FALSE(obj->is_boolean());
+ EXPECT_FALSE(obj->is_integer());
+ EXPECT_FALSE(obj->is_number());
+ EXPECT_TRUE(obj->is_object());
+ EXPECT_FALSE(obj->is_array());
+ EXPECT_FALSE(obj->is_null());
+
+ SrsJsonObject *converted = obj->to_object();
+ EXPECT_TRUE(converted != NULL);
+ EXPECT_EQ(obj, converted);
+ }
+
+ // Test array creation
+ if (true) {
+ SrsJsonArray *arr = SrsJsonAny::array();
+ SrsUniquePtr arr_uptr(arr);
+
+ EXPECT_FALSE(arr->is_string());
+ EXPECT_FALSE(arr->is_boolean());
+ EXPECT_FALSE(arr->is_integer());
+ EXPECT_FALSE(arr->is_number());
+ EXPECT_FALSE(arr->is_object());
+ EXPECT_TRUE(arr->is_array());
+ EXPECT_FALSE(arr->is_null());
+
+ SrsJsonArray *converted = arr->to_array();
+ EXPECT_TRUE(converted != NULL);
+ EXPECT_EQ(arr, converted);
+ }
+}
+
+VOID TEST(ProtocolJsonTest, SrsJsonLoads)
+{
+ // Test loading simple JSON string
+ if (true) {
+ SrsJsonAny *json = SrsJsonAny::loads("{\"name\":\"test\",\"value\":123}");
+ if (json) {
+ SrsUniquePtr json_uptr(json);
+ EXPECT_TRUE(json->is_object());
+ }
+ }
+
+ // Test loading invalid JSON - should return NULL
+ if (true) {
+ SrsJsonAny *json = SrsJsonAny::loads("invalid json");
+ EXPECT_TRUE(json == NULL);
+ }
+
+ // Test loading empty string - should return NULL
+ if (true) {
+ SrsJsonAny *json = SrsJsonAny::loads("");
+ EXPECT_TRUE(json == NULL);
+ }
+}
+
+VOID TEST(ProtocolRawAvcTest, SrsRawH264StreamBasic)
+{
+ SrsRawH264Stream h264;
+
+ // Test basic functionality - these methods should exist and not crash
+ // We can't easily test the full functionality without complex H.264 data
+
+ // Test SPS/PPS detection with minimal data - just test that methods don't crash
+ char test_frame[] = {0x00, 0x00, 0x00, 0x01, 0x67}; // Minimal SPS-like data
+ bool is_sps = h264.is_sps(test_frame, sizeof(test_frame));
+ bool is_pps = h264.is_pps(test_frame, sizeof(test_frame));
+ (void)is_sps;
+ (void)is_pps;
+ // Don't assert specific results since frame detection is complex
+
+ char pps_frame[] = {0x00, 0x00, 0x00, 0x01, 0x68}; // Minimal PPS-like data
+ bool is_sps2 = h264.is_sps(pps_frame, sizeof(pps_frame));
+ bool is_pps2 = h264.is_pps(pps_frame, sizeof(pps_frame));
+ (void)is_sps2;
+ (void)is_pps2;
+ // Don't assert specific results since frame detection is complex
+}
+
+VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamBasic)
+{
+ SrsRawHEVCStream hevc;
+
+ // Test basic functionality for HEVC - just test that methods don't crash
+ // Test VPS/SPS/PPS detection with minimal data
+ char vps_frame[] = {0x00, 0x00, 0x00, 0x01, 0x40}; // Minimal VPS-like data (NALU type 32)
+ char sps_frame[] = {0x00, 0x00, 0x00, 0x01, 0x42}; // Minimal SPS-like data (NALU type 33)
+ char pps_frame[] = {0x00, 0x00, 0x00, 0x01, 0x44}; // Minimal PPS-like data (NALU type 34)
+
+ // Test that methods don't crash - don't assert specific results since frame detection is complex
+ bool is_vps1 = hevc.is_vps(vps_frame, sizeof(vps_frame));
+ bool is_sps1 = hevc.is_sps(vps_frame, sizeof(vps_frame));
+ bool is_pps1 = hevc.is_pps(vps_frame, sizeof(vps_frame));
+ (void)is_vps1;
+ (void)is_sps1;
+ (void)is_pps1;
+
+ bool is_vps2 = hevc.is_vps(sps_frame, sizeof(sps_frame));
+ bool is_sps2 = hevc.is_sps(sps_frame, sizeof(sps_frame));
+ bool is_pps2 = hevc.is_pps(sps_frame, sizeof(sps_frame));
+ (void)is_vps2;
+ (void)is_sps2;
+ (void)is_pps2;
+
+ bool is_vps3 = hevc.is_vps(pps_frame, sizeof(pps_frame));
+ bool is_sps3 = hevc.is_sps(pps_frame, sizeof(pps_frame));
+ bool is_pps3 = hevc.is_pps(pps_frame, sizeof(pps_frame));
+ (void)is_vps3;
+ (void)is_sps3;
+ (void)is_pps3;
+}
+
+VOID TEST(ProtocolHttpClientTest, SrsHttpClientBasic)
+{
+ SrsHttpClient client;
+
+ // Test basic initialization - should not crash
+ // We can't easily test actual HTTP requests without a server
+
+ // Test header setting
+ SrsHttpClient *result = client.set_header("User-Agent", "SRS-Test");
+ EXPECT_TRUE(result != NULL);
+ EXPECT_EQ(&client, result); // Should return self for chaining
+
+ // Test multiple headers
+ client.set_header("Content-Type", "application/json");
+ client.set_header("Accept", "application/json");
+}
+
+VOID TEST(ProtocolStreamTest, SrsFastStreamBasic)
+{
+ SrsFastStream stream;
+
+ // Test initial state
+ EXPECT_EQ(0, stream.size());
+
+ // Test bytes() method - should not crash even when empty
+ char *bytes = stream.bytes();
+ (void)bytes;
+ // bytes might be NULL or valid pointer, both are acceptable for empty stream
+
+ // Test buffer setting
+ stream.set_buffer(1024);
+ EXPECT_EQ(0, stream.size()); // Size should still be 0 after setting buffer
+}
+
+VOID TEST(ProtocolLogTest, SrsThreadContextBasic)
+{
+ SrsThreadContext context;
+
+ // Test ID generation
+ SrsContextId id1 = context.generate_id();
+ SrsContextId id2 = context.generate_id();
+
+ // IDs should be different - compare using compare method
+ EXPECT_TRUE(id1.compare(id2) != 0);
+
+ // Test getting current ID
+ const SrsContextId ¤t = context.get_id();
+ (void)current;
+ // Should not crash
+
+ // Test setting ID
+ SrsContextId new_id = context.generate_id();
+ const SrsContextId &set_result = context.set_id(new_id);
+ EXPECT_EQ(0, new_id.compare(set_result));
+}
+
+VOID TEST(ProtocolLogTest, SrsConsoleLogBasic)
+{
+ // SrsConsoleLog requires parameters: level and utc flag
+ SrsConsoleLog console_log(SrsLogLevelTrace, false);
+
+ // Test basic functionality - should not crash
+ // We can't easily test actual logging without capturing output
+
+ // Test initialization
+ srs_error_t err = console_log.initialize();
+ HELPER_EXPECT_SUCCESS(err);
+
+ // Test reopen - should not crash
+ console_log.reopen();
+
+ // The console log should be constructible and destructible without issues
+ EXPECT_TRUE(true); // Just verify we can create and destroy the object
+}
+
+VOID TEST(ProtocolRtmpConnTest, SrsBasicRtmpClientBasic)
+{
+ // Test basic RTMP client construction
+ SrsBasicRtmpClient client("rtmp://127.0.0.1:1935/live/test", 3000 * SRS_UTIME_MILLISECONDS, 9000 * SRS_UTIME_MILLISECONDS);
+
+ // Test stream ID - should be 0 initially
+ EXPECT_EQ(0, client.sid());
+
+ // Test extra args access
+ SrsAmf0Object *args = client.extra_args();
+ EXPECT_TRUE(args != NULL);
+
+ // Note: We don't test set_recv_timeout() here because it requires a valid socket connection
+ // which we don't have in unit tests. The method exists and will be tested in integration tests.
+}
+
+VOID TEST(ProtocolStTest, SrsStInitDestroy)
+{
+ // Test ST initialization and destruction
+ // Note: We can't easily test the full ST functionality without proper setup
+ // but we can test that the functions exist and don't crash
+
+ srs_error_t err = srs_st_init();
+ if (err == srs_success) {
+ // If init succeeds, test destroy
+ srs_st_destroy();
+ }
+ // If init fails, that's also acceptable in test environment
+}
+
+VOID TEST(ProtocolStTest, SrsStSocketBasic)
+{
+ // Test basic socket operations - these should exist and not crash
+ // We can't easily test full socket functionality without network setup
+
+ // Test socket creation functions exist
+ srs_netfd_t fd = NULL;
+ EXPECT_TRUE(fd == NULL);
+
+ // Test that we can call socket utility functions
+ bool is_never_timeout = srs_is_never_timeout(SRS_UTIME_NO_TIMEOUT);
+ EXPECT_TRUE(is_never_timeout);
+
+ bool is_not_never_timeout = srs_is_never_timeout(1000 * SRS_UTIME_MILLISECONDS);
+ EXPECT_FALSE(is_not_never_timeout);
+}
+
+VOID TEST(ProtocolConnTest, SrsTcpConnectionInterface)
+{
+ // We can't easily create a real TCP connection in unit tests
+ // but we can test that the class interface is properly defined
+
+ // Test that we can create a NULL connection (will fail but shouldn't crash)
+ // This tests the interface exists
+ SrsTcpConnection *conn = new SrsTcpConnection(NULL);
+
+ // The connection should be created (even with invalid fd)
+ EXPECT_TRUE(conn != NULL);
+
+ // Clean up
+ delete conn;
+}
+
+VOID TEST(ProtocolConnTest, SrsSslConnectionInterface)
+{
+ // Test SSL connection interface
+ // We can't easily test full SSL functionality without certificates
+
+ // Create a TCP connection first (with invalid fd for testing)
+ SrsTcpConnection *tcp = new SrsTcpConnection(NULL);
+
+ // Create SSL connection wrapper
+ SrsSslConnection *ssl = new SrsSslConnection(tcp);
+
+ // The SSL connection should be created
+ EXPECT_TRUE(ssl != NULL);
+
+ // Test timeout methods exist
+ ssl->set_recv_timeout(1000 * SRS_UTIME_MILLISECONDS);
+ srs_utime_t timeout = ssl->get_recv_timeout();
+ EXPECT_EQ(1000 * SRS_UTIME_MILLISECONDS, timeout);
+
+ ssl->set_send_timeout(2000 * SRS_UTIME_MILLISECONDS);
+ srs_utime_t send_timeout = ssl->get_send_timeout();
+ EXPECT_EQ(2000 * SRS_UTIME_MILLISECONDS, send_timeout);
+
+ // Test byte counters
+ EXPECT_EQ(0, ssl->get_recv_bytes());
+ EXPECT_EQ(0, ssl->get_send_bytes());
+
+ // Clean up
+ delete ssl; // This will also delete the tcp connection
+}