From 7684a717decc5fb10c763aedee1ea24564dff2bd Mon Sep 17 00:00:00 2001 From: Slava Monich Date: Sat, 11 Sep 2021 16:19:05 +0300 Subject: [PATCH] [gbinder-radio] Added unit tests. JB#55524 --- .gitignore | 2 + Makefile | 16 +- rpm/libgbinder-radio.spec | 9 +- unit/Makefile | 11 + unit/common/Makefile | 217 ++++++ unit/common/test_common.h | 108 +++ unit/common/test_gbinder.h | 161 +++++ unit/common/test_gbinder_client.c | 366 ++++++++++ unit/common/test_gbinder_local_object.c | 153 ++++ unit/common/test_gbinder_local_reply.c | 118 ++++ unit/common/test_gbinder_local_request.c | 141 ++++ unit/common/test_gbinder_reader_writer.c | 324 +++++++++ unit/common/test_gbinder_remote_object.c | 180 +++++ unit/common/test_gbinder_remote_reply.c | 106 +++ unit/common/test_gbinder_remote_request.c | 119 ++++ unit/common/test_gbinder_servicemanager.c | 180 +++++ unit/common/test_main.c | 166 +++++ unit/coverage/run | 54 ++ unit/unit_instance/Makefile | 5 + unit/unit_instance/unit_instance.c | 820 ++++++++++++++++++++++ unit/unit_registry/Makefile | 5 + unit/unit_registry/unit_registry.c | 170 +++++ unit/unit_util/Makefile | 5 + unit/unit_util/unit_util.c | 162 +++++ 24 files changed, 3595 insertions(+), 3 deletions(-) create mode 100644 unit/Makefile create mode 100644 unit/common/Makefile create mode 100644 unit/common/test_common.h create mode 100644 unit/common/test_gbinder.h create mode 100644 unit/common/test_gbinder_client.c create mode 100644 unit/common/test_gbinder_local_object.c create mode 100644 unit/common/test_gbinder_local_reply.c create mode 100644 unit/common/test_gbinder_local_request.c create mode 100644 unit/common/test_gbinder_reader_writer.c create mode 100644 unit/common/test_gbinder_remote_object.c create mode 100644 unit/common/test_gbinder_remote_reply.c create mode 100644 unit/common/test_gbinder_remote_request.c create mode 100644 unit/common/test_gbinder_servicemanager.c create mode 100644 unit/common/test_main.c create mode 100755 unit/coverage/run create mode 100644 unit/unit_instance/Makefile create mode 100644 unit/unit_instance/unit_instance.c create mode 100644 unit/unit_registry/Makefile create mode 100644 unit/unit_registry/unit_registry.c create mode 100644 unit/unit_util/Makefile create mode 100644 unit/unit_util/unit_util.c diff --git a/.gitignore b/.gitignore index 96762b4..cd9e487 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ debian/*.substvars debian/*.install debian/tmp documentation.list +unit/coverage/*.gcov +unit/coverage/report installroot build RPMS diff --git a/Makefile b/Makefile index 33b5014..24dd0ac 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ # -*- Mode: makefile-gmake -*- -.PHONY: clean all debug release coverage +.PHONY: clean all debug release coverage test .PHONY: debug_lib release_lib coverage_lib +.PHONY: print_debug_lib print_release_lib print_coverage_lib .PHONY: pkgconfig install install-dev @@ -138,7 +139,17 @@ coverage_lib: $(COVERAGE_LIB) pkgconfig: $(PKGCONFIG) +print_debug_lib: + @echo $(DEBUG_LIB) + +print_release_lib: + @echo $(RELEASE_LIB) + +print_coverage_lib: + @echo $(COVERAGE_LIB) + clean: + make -C unit clean rm -f *~ $(SRC_DIR)/*~ $(INCLUDE_DIR)/*~ rm -fr $(BUILD_DIR) RPMS installroot rm -fr debian/tmp debian/libgbinder-radio debian/libgbinder-radio-dev @@ -146,6 +157,9 @@ clean: rm -f debian/*.debhelper.log debian/*.debhelper debian/*~ rm -f debian/*.install +test: + make -C unit test + $(BUILD_DIR): mkdir -p $@ diff --git a/rpm/libgbinder-radio.spec b/rpm/libgbinder-radio.spec index 5c039fe..c8c03a8 100644 --- a/rpm/libgbinder-radio.spec +++ b/rpm/libgbinder-radio.spec @@ -8,10 +8,12 @@ URL: https://github.com/mer-hybris/libgbinder-radio Source: %{name}-%{version}.tar.bz2 %define libgbinder_version 1.0.9 +%define libglibutil_version 1.0.34 BuildRequires: pkgconfig(glib-2.0) -BuildRequires: pkgconfig(libglibutil) +BuildRequires: pkgconfig(libglibutil) >= %{libglibutil_version} BuildRequires: pkgconfig(libgbinder) >= %{libgbinder_version} +Requires: libglibutil >= %{libglibutil_version} Requires: libgbinder >= %{libgbinder_version} Requires(post): /sbin/ldconfig Requires(postun): /sbin/ldconfig @@ -31,12 +33,15 @@ This package contains the development library for %{name}. %setup -q %build -make LIBDIR=%{_libdir} KEEP_SYMBOLS=1 release pkgconfig +make %{_smp_mflags} LIBDIR=%{_libdir} KEEP_SYMBOLS=1 release pkgconfig %install rm -rf %{buildroot} make LIBDIR=%{_libdir} DESTDIR=%{buildroot} install-dev +%check +make -C unit test + %post -p /sbin/ldconfig %postun -p /sbin/ldconfig diff --git a/unit/Makefile b/unit/Makefile new file mode 100644 index 0000000..58ec093 --- /dev/null +++ b/unit/Makefile @@ -0,0 +1,11 @@ +# -*- Mode: makefile-gmake -*- + +all: +%: + @$(MAKE) -C unit_instance $* + @$(MAKE) -C unit_registry $* + @$(MAKE) -C unit_util $* + +clean: unitclean + rm -f coverage/*.gcov + rm -fr coverage/report diff --git a/unit/common/Makefile b/unit/common/Makefile new file mode 100644 index 0000000..ff5886c --- /dev/null +++ b/unit/common/Makefile @@ -0,0 +1,217 @@ +# -*- Mode: makefile-gmake -*- + +.PHONY: clean all debug release coverage +.PHONY: debug_lib release_lib coverage_lib + +# +# Real test makefile defines EXE (and possibly SRC) and includes this one. +# + +ifndef EXE +${error EXE not defined} +endif + +SRC ?= $(EXE).c +COMMON_SRC ?= \ + test_gbinder_client.c \ + test_gbinder_local_object.c \ + test_gbinder_local_request.c \ + test_gbinder_local_reply.c \ + test_gbinder_reader_writer.c \ + test_gbinder_remote_object.c \ + test_gbinder_remote_request.c \ + test_gbinder_remote_reply.c \ + test_gbinder_servicemanager.c \ + test_main.c + +# +# Required packages +# + +LINK_PKGS += libglibutil glib-2.0 gobject-2.0 +PKGS += $(LINK_PKGS) libgbinder + +# +# Default target +# + +all: debug release + +# +# Directories +# + +SRC_DIR = . +LIB_DIR = ../.. +COMMON_DIR = ../common +BUILD_DIR = build +DEBUG_BUILD_DIR = $(BUILD_DIR)/debug +RELEASE_BUILD_DIR = $(BUILD_DIR)/release +COVERAGE_BUILD_DIR = $(BUILD_DIR)/coverage +COMMON_BUILD_DIR = $(COMMON_DIR)/build +COMMON_DEBUG_BUILD_DIR = $(COMMON_BUILD_DIR)/debug +COMMON_RELEASE_BUILD_DIR = $(COMMON_BUILD_DIR)/release +COMMON_COVERAGE_BUILD_DIR = $(COMMON_BUILD_DIR)/coverage + +# +# Tools and flags +# + +CC ?= $(CROSS_COMPILE)gcc +LD = $(CC) +WARNINGS += -Wall -Wno-deprecated-declarations +INCLUDES += -I$(COMMON_DIR) -I$(LIB_DIR)/src -I$(LIB_DIR)/include +BASE_FLAGS = -fPIC +BASE_LDFLAGS = $(BASE_FLAGS) $(LDFLAGS) +BASE_CFLAGS = $(BASE_FLAGS) $(CFLAGS) +FULL_CFLAGS = $(BASE_CFLAGS) $(DEFINES) $(WARNINGS) $(INCLUDES) -MMD -MP \ + $(shell pkg-config --cflags $(PKGS)) +FULL_LDFLAGS = $(BASE_LDFLAGS) +LIBS = $(shell pkg-config --libs $(LINK_PKGS)) -lpthread +QUIET_MAKE = make --no-print-directory +DEBUG_FLAGS = -g +RELEASE_FLAGS = +COVERAGE_FLAGS = -g + +DEBUG_LDFLAGS = $(FULL_LDFLAGS) $(DEBUG_FLAGS) +RELEASE_LDFLAGS = $(FULL_LDFLAGS) $(RELEASE_FLAGS) +COVERAGE_LDFLAGS = $(FULL_LDFLAGS) $(COVERAGE_FLAGS) --coverage + +DEBUG_CFLAGS = $(FULL_CFLAGS) $(DEBUG_FLAGS) -DDEBUG +RELEASE_CFLAGS = $(FULL_CFLAGS) $(RELEASE_FLAGS) -O2 +COVERAGE_CFLAGS = $(FULL_CFLAGS) $(COVERAGE_FLAGS) --coverage + +DEBUG_LIB_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_lib) +RELEASE_LIB_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_lib) +COVERAGE_LIB_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_coverage_lib) + +DEBUG_LIB = $(LIB_DIR)/$(DEBUG_LIB_FILE) +RELEASE_LIB = $(LIB_DIR)/$(RELEASE_LIB_FILE) +COVERAGE_LIB = $(LIB_DIR)/$(COVERAGE_LIB_FILE) + +DEBUG_LIBS = $(DEBUG_LIB) $(LIBS) +RELEASE_LIBS = $(RELEASE_LIB) $(LIBS) +COVERAGE_LIBS = $(COVERAGE_LIB) $(LIBS) + +# +# Files +# + +TEST_DEBUG_OBJS = $(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o) +TEST_RELEASE_OBJS = $(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o) +TEST_COVERAGE_OBJS = $(SRC:%.c=$(COVERAGE_BUILD_DIR)/%.o) +COMMON_DEBUG_OBJS = $(COMMON_SRC:%.c=$(COMMON_DEBUG_BUILD_DIR)/%.o) +COMMON_RELEASE_OBJS = $(COMMON_SRC:%.c=$(COMMON_RELEASE_BUILD_DIR)/%.o) +COMMON_COVERAGE_OBJS = $(COMMON_SRC:%.c=$(COMMON_COVERAGE_BUILD_DIR)/%.o) +DEBUG_OBJS = $(COMMON_DEBUG_OBJS) $(TEST_DEBUG_OBJS) +RELEASE_OBJS = $(COMMON_RELEASE_OBJS) $(TEST_RELEASE_OBJS) +COVERAGE_OBJS = $(COMMON_COVERAGE_OBJS) $(TEST_COVERAGE_OBJS) + +# +# Dependencies +# + +DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d) +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(strip $(DEPS)),) +-include $(DEPS) +endif +endif + +$(DEBUG_LIB): | debug_lib +$(RELEASE_LIB): | release_lib +$(COVERAGE_LIB): | coverage_lib + +$(TEST_DEBUG_OBJS): | $(DEBUG_BUILD_DIR) +$(TEST_RELEASE_OBJS): | $(RELEASE_BUILD_DIR) +$(TEST_COVERAGE_OBJS): | $(COVERAGE_BUILD_DIR) + +$(COMMON_DEBUG_OBJS): | $(COMMON_DEBUG_BUILD_DIR) +$(COMMON_RELEASE_OBJS): | $(COMMON_RELEASE_BUILD_DIR) +$(COMMON_COVERAGE_OBJS): | $(COMMON_COVERAGE_BUILD_DIR) + +# +# Rules +# + +DEBUG_EXE = $(DEBUG_BUILD_DIR)/$(EXE) +RELEASE_EXE = $(RELEASE_BUILD_DIR)/$(EXE) +COVERAGE_EXE = $(COVERAGE_BUILD_DIR)/$(EXE) + +debug: debug_lib $(DEBUG_EXE) + +release: release_lib $(RELEASE_EXE) + +coverage: coverage_lib $(COVERAGE_EXE) + +unitclean: + rm -f *~ + rm -fr $(BUILD_DIR) $(COMMON_BUILD_DIR) + +clean: unitclean + +cleaner: unitclean + @make -C $(LIB_DIR) clean + +test_banner: + @echo "===========" $(EXE) "=========== " + +test: test_banner debug + @$(DEBUG_EXE) + +valgrind: test_banner debug + @G_DEBUG=gc-friendly G_SLICE=always-malloc valgrind --tool=memcheck --leak-check=full --show-possibly-lost=no $(DEBUG_EXE) + +$(DEBUG_BUILD_DIR): + mkdir -p $@ + +$(RELEASE_BUILD_DIR): + mkdir -p $@ + +$(COVERAGE_BUILD_DIR): + mkdir -p $@ + +$(COMMON_DEBUG_BUILD_DIR): + mkdir -p $@ + +$(COMMON_RELEASE_BUILD_DIR): + mkdir -p $@ + +$(COMMON_COVERAGE_BUILD_DIR): + mkdir -p $@ + +$(DEBUG_BUILD_DIR)/%.o : $(SRC_DIR)/%.c + $(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ + +$(RELEASE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c + $(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ + +$(COVERAGE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c + $(CC) -c $(COVERAGE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ + +$(COMMON_DEBUG_BUILD_DIR)/%.o : $(COMMON_DIR)/%.c + $(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ + +$(COMMON_RELEASE_BUILD_DIR)/%.o : $(COMMON_DIR)/%.c + $(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ + +$(COMMON_COVERAGE_BUILD_DIR)/%.o : $(COMMON_DIR)/%.c + $(CC) -c $(COVERAGE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ + +$(DEBUG_EXE): $(DEBUG_LIB) $(DEBUG_OBJS) + $(LD) $(DEBUG_LDFLAGS) $(DEBUG_OBJS) $(DEBUG_LIBS) -o $@ + +$(RELEASE_EXE): $(RELEASE_LIB) $(RELEASE_OBJS) + $(LD) $(RELEASE_LDFLAGS) $(RELEASE_OBJS) $(RELEASE_LIBS) -o $@ + +$(COVERAGE_EXE): $(COVERAG_LIB) $(COVERAGE_OBJS) + $(LD) $(COVERAGE_LDFLAGS) $(COVERAGE_OBJS) $(COVERAGE_LIBS) -o $@ + +debug_lib: + $(MAKE) -C $(LIB_DIR) $@ + +release_lib: + $(MAKE) -C $(LIB_DIR) $@ + +coverage_lib: + $(MAKE) -C $(LIB_DIR) $@ diff --git a/unit/common/test_common.h b/unit/common/test_common.h new file mode 100644 index 0000000..f0a1ca5 --- /dev/null +++ b/unit/common/test_common.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich + * + * You may use this file under the terms of BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TEST_COMMON_H +#define TEST_COMMON_H + +#include + +#define TEST_FLAG_DEBUG (0x01) +typedef struct test_opt { + int flags; +} TestOpt; + +/* Should be invoked after g_test_init */ +void +test_init( + TestOpt* opt, + int argc, + char* argv[]); + +/* Run loop with a timeout */ +void +test_run( + const TestOpt* opt, + GMainLoop* loop); + +/* Quits the event loop on the next iteration */ +void +test_quit_later( + GMainLoop* loop); + +/* Quits the event loop after n iterations */ +void +test_quit_later_n( + GMainLoop* loop, + guint n); + +#define TEST_TIMEOUT_SEC (20) + +/* Helper macros */ + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +# define TEST_INT16_BYTES(v) \ + (guint8)(v), (guint8)((v) >> 8) +# define TEST_INT32_BYTES(v) \ + (guint8)(v), (guint8)((v) >> 8), \ + (guint8)((v) >> 16), (guint8)((v) >> 24) +# define TEST_INT64_BYTES(v) \ + (guint8)(v), (guint8)((v) >> 8), \ + (guint8)((v) >> 16), (guint8)((v) >> 24), \ + (guint8)(((guint64)(v)) >> 32), (guint8)(((guint64)(v)) >> 40), \ + (guint8)(((guint64)(v)) >> 48), (guint8)(((guint64)(v)) >> 56) +#elif G_BYTE_ORDER == G_BIG_ENDIAN +# define TEST_INT16_BYTES(v) \ + (guint8)((v) >> 8), (guint8)(v) +# define TEST_INT32_BYTES(v) \ + (guint8)((v) >> 24), (guint8)((v) >> 16), \ + (guint8)((v) >> 8), (guint8)(v) +# define TEST_INT64_BYTES(v) \ + (guint8)(((guint64)(v)) >> 56), (guint8)(((guint64)(v)) >> 48), \ + (guint8)(((guint64)(v)) >> 40), (guint8)(((guint64)(v)) >> 32), \ + (guint8)((v) >> 24), (guint8)((v) >> 16), \ + (guint8)((v) >> 8), (guint8)(v) +#else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */ +#error unknown ENDIAN type +#endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */ + +#define TEST_ARRAY_AND_COUNT(a) a, G_N_ELEMENTS(a) +#define TEST_ARRAY_AND_SIZE(a) a, sizeof(a) + +#endif /* TEST_COMMON_H */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_gbinder.h b/unit/common/test_gbinder.h new file mode 100644 index 0000000..225a4f2 --- /dev/null +++ b/unit/common/test_gbinder.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TEST_GBINDER_H +#define TEST_GBINDER_H + +#include + +typedef struct test_gbinder_data TestGBinderData; + +/* test_gbinder_reader_writer.c */ + +TestGBinderData* +test_gbinder_data_new( + void); + +TestGBinderData* +test_gbinder_data_ref( + TestGBinderData* data); + +void +test_gbinder_data_unref( + TestGBinderData* data); + +void +test_gbinder_data_add_int32( + TestGBinderData* data, + guint32 value); + +void +test_gbinder_data_add_hidl_struct( + TestGBinderData* data, + const void* buf, + gsize size); + +void +test_gbinder_data_init_reader( + TestGBinderData* data, + GBinderReader* reader); + +void +test_gbinder_data_init_writer( + TestGBinderData* data, + GBinderWriter* writer); + +/* test_gbinder_local_request.c */ + +GBinderLocalRequest* +test_gbinder_local_request_new( + const char* iface); + +const char* +test_gbinder_local_request_interface( + GBinderLocalRequest* local); + +TestGBinderData* +test_gbinder_local_request_data( + GBinderLocalRequest* local); + +/* test_gbinder_local_reply.c */ + +GBinderLocalReply* +test_gbinder_local_reply_new( + void); + +TestGBinderData* +test_gbinder_local_reply_data( + GBinderLocalReply* reply); + +/* test_gbinder_remote_request.c */ + +GBinderRemoteRequest* +test_gbinder_remote_request_new( + GBinderLocalRequest* req); + +/* test_gbinder_remote_reply.c */ + +GBinderRemoteReply* +test_gbinder_remote_reply_new( + GBinderLocalReply* reply); + +/* test_gbinder_local_object.c */ + +GBinderLocalObject* +test_gbinder_local_object_new( + const char* const* ifaces, + GBinderLocalTransactFunc txproc, + void* user_data); + +GBinderLocalReply* +test_gbinder_local_object_handle_tx( + GBinderLocalObject* self, + GBinderRemoteRequest* req, + guint code, + guint flags, + int* status); + +/* test_gbinder_remote_object.c */ + +GBinderRemoteObject* +test_gbinder_remote_object_new( + GBinderLocalObject* local); + +void +test_gbinder_remote_object_kill( + GBinderRemoteObject* remote); + +gboolean +test_gbinder_remote_object_dead( + GBinderRemoteObject* remote); + +GBinderLocalObject* +test_gbinder_remote_object_to_local( + GBinderRemoteObject* remote); + +/* test_gbinder_servicemanager.c */ + +GBinderRemoteObject* +test_gbinder_servicemanager_new_service( + GBinderServiceManager* manager, + const char* name, + GBinderLocalObject* local); + +#endif /* TEST_GBINDER_H */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_gbinder_client.c b/unit/common/test_gbinder_client.c new file mode 100644 index 0000000..38d1538 --- /dev/null +++ b/unit/common/test_gbinder_client.c @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_gbinder.h" + +#include + +typedef struct test_gbinder_client_tx { + GBinderClient* client; + guint32 code; + guint32 flags; + GBinderLocalRequest* req; + GBinderClientReplyFunc reply; + GDestroyNotify destroy; + void* user_data; +} TestGBinderClientTx; + +typedef struct test_gbinder_client_iface_range { + char* iface; + guint32 last_code; +} TestGBinderClientIfaceRange; + +struct gbinder_client { + guint32 refcount; + GBinderRemoteObject* remote; + TestGBinderClientIfaceRange* ranges; + guint nr; +}; + +static +void +test_gbinder_client_free( + GBinderClient* self) +{ + guint i; + + for (i = 0; i < self->nr; i++) { + g_free(self->ranges[i].iface); + } + g_free(self->ranges); + gbinder_remote_object_unref(self->remote); + g_free(self); +} + +static +void +test_gbinder_client_init_range( + TestGBinderClientIfaceRange* r, + const GBinderClientIfaceInfo* info) +{ + r->iface = g_strdup(info->iface); + r->last_code = info->last_code; +} + +static +int +test_gbinder_client_sort_ranges( + const void* p1, + const void* p2) +{ + const TestGBinderClientIfaceRange* r1 = p1; + const TestGBinderClientIfaceRange* r2 = p2; + + return (r1->last_code < r2->last_code) ? (-1) : + (r1->last_code > r2->last_code) ? 1 : 0; +} + +static +const TestGBinderClientIfaceRange* +test_gbinder_client_find_range( + GBinderClient* self, + guint32 code) +{ + guint i; + + for (i = 0; i < self->nr; i++) { + const TestGBinderClientIfaceRange* r = self->ranges + i; + + if (r->last_code >= code) { + return r; + } + } + return NULL; +} + +static +GBinderRemoteReply* +test_gbinder_client_transact( + GBinderClient* self, + guint32 code, + guint32 flags, + GBinderLocalRequest* req, + int* status) +{ + GBinderLocalObject* obj = test_gbinder_remote_object_to_local(self->remote); + GBinderRemoteRequest* remote_req = test_gbinder_remote_request_new(req); + GBinderLocalReply* reply = test_gbinder_local_object_handle_tx(obj, + remote_req, code, flags, status); + GBinderRemoteReply* remote_reply = test_gbinder_remote_reply_new(reply); + + gbinder_remote_request_unref(remote_req); + gbinder_local_reply_unref(reply); + return remote_reply; +} + +static +GBinderRemoteReply* +test_gbinder_client_transact_sync( + GBinderClient* self, + guint32 code, + guint32 flags, + GBinderLocalRequest* req, + int* status) +{ + GBinderRemoteReply* reply = NULL; + + if (self) { + GBinderRemoteObject* obj = self->remote; + + if (!test_gbinder_remote_object_dead(obj)) { + GBinderLocalRequest* tmp = NULL; + + if (!req) { + const TestGBinderClientIfaceRange* r = + test_gbinder_client_find_range(self, code); + + if (r) { + req = tmp = test_gbinder_local_request_new(r->iface); + } + } + if (req) { + reply = test_gbinder_client_transact(self, code, flags, req, + status); + } + gbinder_local_request_unref(tmp); + } else { + GDEBUG("Refusing to perform transaction with a dead object"); + } + } + return reply; +} + +static +gboolean +test_gbinder_client_tx_handle( + gpointer data) +{ + int status = -1; + TestGBinderClientTx* tx = data; + GBinderRemoteReply* reply = test_gbinder_client_transact + (tx->client, tx->code, tx->flags, tx->req, &status); + + tx->reply(tx->client, reply, status, tx->user_data); + gbinder_remote_reply_unref(reply); + return G_SOURCE_REMOVE; +} + +static +void +test_gbinder_client_tx_destroy( + gpointer data) +{ + TestGBinderClientTx* tx = data; + + if (tx->destroy) { + tx->destroy(tx->user_data); + } + gbinder_local_request_unref(tx->req); + gbinder_client_unref(tx->client); + g_free(tx); +} + +/*==========================================================================* + * libgbinder API + *==========================================================================*/ + +GBinderClient* +gbinder_client_new2( + GBinderRemoteObject* remote, + const GBinderClientIfaceInfo* ifaces, + gsize count) +{ + if (remote) { + GBinderClient* self = g_new0(GBinderClient, 1); + + g_atomic_int_set(&self->refcount, 1); + self->remote = gbinder_remote_object_ref(remote); + if (count > 0) { + gsize i; + + self->nr = count; + self->ranges = g_new(TestGBinderClientIfaceRange, self->nr); + for (i = 0; i < count; i++) { + test_gbinder_client_init_range(self->ranges + i, ifaces + i); + } + qsort(self->ranges, count, sizeof(TestGBinderClientIfaceRange), + test_gbinder_client_sort_ranges); + } else { + /* No interface info */ + self->nr = 1; + self->ranges = g_new0(TestGBinderClientIfaceRange, 1); + self->ranges[0].last_code = UINT_MAX; + } + return self; + } + return NULL; +} + +GBinderClient* +gbinder_client_ref( + GBinderClient* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + g_atomic_int_inc(&self->refcount); + } + return self; +} + +void +gbinder_client_unref( + GBinderClient* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + if (g_atomic_int_dec_and_test(&self->refcount)) { + test_gbinder_client_free(self); + } + } +} + +GBinderLocalRequest* +gbinder_client_new_request2( + GBinderClient* self, + guint32 code) +{ + if (self) { + const TestGBinderClientIfaceRange* r = + test_gbinder_client_find_range(self, code); + + if (r) { + return test_gbinder_local_request_new(r->iface); + } + } + return NULL; +} + +GBinderRemoteReply* +gbinder_client_transact_sync_reply( + GBinderClient* self, + guint32 code, + GBinderLocalRequest* req, + int* status) +{ + return test_gbinder_client_transact_sync(self, code, 0, req, status); +} + +int +gbinder_client_transact_sync_oneway( + GBinderClient* self, + guint32 code, + GBinderLocalRequest* req) +{ + int status = -1; + + g_assert(!test_gbinder_client_transact_sync(self, code, + GBINDER_TX_FLAG_ONEWAY, req, &status)); + return status; +} + +gulong +gbinder_client_transact( + GBinderClient* self, + guint32 code, + guint32 flags, + GBinderLocalRequest* req, + GBinderClientReplyFunc reply, + GDestroyNotify destroy, + void* user_data) +{ + gulong id = 0; + + if (self) { + GBinderRemoteObject* obj = self->remote; + + if (!test_gbinder_remote_object_dead(obj)) { + GBinderLocalRequest* tmp = NULL; + + if (!req) { + const TestGBinderClientIfaceRange* r = + test_gbinder_client_find_range(self, code); + + if (r) { + req = tmp = test_gbinder_local_request_new(r->iface); + } + } + if (req) { + TestGBinderClientTx* tx = g_new0(TestGBinderClientTx, 1); + + tx->client = gbinder_client_ref(self); + tx->code = code; + tx->flags = flags; + tx->req = gbinder_local_request_ref(req); + tx->reply = reply; + tx->destroy = destroy; + tx->user_data = user_data; + id = g_idle_add_full(G_PRIORITY_DEFAULT, + test_gbinder_client_tx_handle, tx, + test_gbinder_client_tx_destroy); + } + gbinder_local_request_unref(tmp); + } else { + GDEBUG("Refusing to perform transaction with a dead object"); + } + } + return id; +} + +void +gbinder_client_cancel( + GBinderClient* self, + gulong id) +{ + g_source_remove((guint)id); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_gbinder_local_object.c b/unit/common/test_gbinder_local_object.c new file mode 100644 index 0000000..a5ed346 --- /dev/null +++ b/unit/common/test_gbinder_local_object.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_gbinder.h" + +#include + +struct gbinder_local_object { + guint32 refcount; + char** ifaces; + GBinderLocalTransactFunc txproc; + void* user_data; +}; + +static const char hidl_base_interface[] = "android.hidl.base@1.0::IBase"; + +static +void +test_gbinder_local_object_free( + GBinderLocalObject* self) +{ + g_strfreev(self->ifaces); + g_free(self); +} + +/*==========================================================================* + * Internal API + *==========================================================================*/ + +GBinderLocalObject* +test_gbinder_local_object_new( + const char* const* ifaces, + GBinderLocalTransactFunc txproc, + void* user_data) +{ + GBinderLocalObject* self = g_new0(GBinderLocalObject, 1); + guint i = 0, n = gutil_strv_length((char**)ifaces); + gboolean append_base_interface; + + if (g_strcmp0(gutil_strv_last((char**)ifaces), hidl_base_interface)) { + append_base_interface = TRUE; + n++; + } else { + append_base_interface = FALSE; + } + + self->ifaces = g_new(char*, n + 1); + if (ifaces) { + while (*ifaces) { + self->ifaces[i++] = g_strdup(*ifaces++); + } + } + if (append_base_interface) { + self->ifaces[i++] = g_strdup(hidl_base_interface); + } + self->ifaces[i] = NULL; + self->txproc = txproc; + self->user_data = user_data; + g_atomic_int_set(&self->refcount, 1); + return self; +} + +GBinderLocalReply* +test_gbinder_local_object_handle_tx( + GBinderLocalObject* self, + GBinderRemoteRequest* req, + guint code, + guint flags, + int* status) +{ + return (self && self->txproc) ? + self->txproc(self, req, code, flags, status, self->user_data) : + NULL; +} + +/*==========================================================================* + * libgbinder API + *==========================================================================*/ + +GBinderLocalObject* +gbinder_local_object_ref( + GBinderLocalObject* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + g_atomic_int_inc(&self->refcount); + } + return self; +} + +void +gbinder_local_object_unref( + GBinderLocalObject* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + if (g_atomic_int_dec_and_test(&self->refcount)) { + test_gbinder_local_object_free(self); + } + } +} + +void +gbinder_local_object_drop( + GBinderLocalObject* self) +{ + if (self) { + self->txproc = NULL; + self->user_data = NULL; + gbinder_local_object_unref(self); + } +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_gbinder_local_reply.c b/unit/common/test_gbinder_local_reply.c new file mode 100644 index 0000000..1d5b254 --- /dev/null +++ b/unit/common/test_gbinder_local_reply.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_gbinder.h" + +struct gbinder_local_reply { + guint32 refcount; + TestGBinderData* data; + char* iface; +}; + +static +void +test_gbinder_local_reply_free( + GBinderLocalReply* self) +{ + test_gbinder_data_unref(self->data); + g_free(self->iface); + g_free(self); +} + +/*==========================================================================* + * Internal API + *==========================================================================*/ + +GBinderLocalReply* +test_gbinder_local_reply_new( + void) +{ + GBinderLocalReply* self = g_new0(GBinderLocalReply, 1); + + g_atomic_int_set(&self->refcount, 1); + self->data = test_gbinder_data_new(); + return self; +} + +TestGBinderData* +test_gbinder_local_reply_data( + GBinderLocalReply* self) +{ + return self ? self->data : NULL; +} + +/*==========================================================================* + * libgbinder API + *==========================================================================*/ + +GBinderLocalReply* +gbinder_local_reply_ref( + GBinderLocalReply* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + g_atomic_int_inc(&self->refcount); + } + return self; +} + +void +gbinder_local_reply_unref( + GBinderLocalReply* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + if (g_atomic_int_dec_and_test(&self->refcount)) { + test_gbinder_local_reply_free(self); + } + } +} + +void +gbinder_local_reply_init_writer( + GBinderLocalReply* self, + GBinderWriter* writer) +{ + test_gbinder_data_init_writer(self->data, writer); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_gbinder_local_request.c b/unit/common/test_gbinder_local_request.c new file mode 100644 index 0000000..4774802 --- /dev/null +++ b/unit/common/test_gbinder_local_request.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_gbinder.h" + +struct gbinder_local_request { + guint32 refcount; + TestGBinderData* data; + char* iface; +}; + +static +void +test_gbinder_local_request_free( + GBinderLocalRequest* self) +{ + test_gbinder_data_unref(self->data); + g_free(self->iface); + g_free(self); +} + +/*==========================================================================* + * Internal API + *==========================================================================*/ + +GBinderLocalRequest* +test_gbinder_local_request_new( + const char* iface) +{ + GBinderLocalRequest* self = g_new0(GBinderLocalRequest, 1); + + g_assert(iface); + g_atomic_int_set(&self->refcount, 1); + self->data = test_gbinder_data_new(); + self->iface = g_strdup(iface); + return self; +} + +const char* +test_gbinder_local_request_interface( + GBinderLocalRequest* self) +{ + return self ? self->iface : NULL; +} + +TestGBinderData* +test_gbinder_local_request_data( + GBinderLocalRequest* self) +{ + return self ? self->data : NULL; +} + +/*==========================================================================* + * libgbinder API + *==========================================================================*/ + +GBinderLocalRequest* +gbinder_local_request_ref( + GBinderLocalRequest* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + g_atomic_int_inc(&self->refcount); + } + return self; +} + +void +gbinder_local_request_unref( + GBinderLocalRequest* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + if (g_atomic_int_dec_and_test(&self->refcount)) { + test_gbinder_local_request_free(self); + } + } +} + +void +gbinder_local_request_init_writer( + GBinderLocalRequest* self, + GBinderWriter* writer) +{ + test_gbinder_data_init_writer(self->data, writer); +} + +GBinderLocalRequest* +gbinder_local_request_append_int32( + GBinderLocalRequest* self, + guint32 value) +{ + if (self) { + GBinderWriter writer; + + test_gbinder_data_init_writer(self->data, &writer); + gbinder_writer_append_int32(&writer, value); + } + return self; +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_gbinder_reader_writer.c b/unit/common/test_gbinder_reader_writer.c new file mode 100644 index 0000000..d39f473 --- /dev/null +++ b/unit/common/test_gbinder_reader_writer.c @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_gbinder.h" + +#include + +typedef enum test_gbinder_data_type { + DATA_TYPE_INT32, + DATA_TYPE_BUFFER, + DATA_TYPE_LOCAL_OBJ +} DATA_TYPE; + +typedef struct test_gbinder_data_item TestGBinderDataItem; +struct test_gbinder_data_item { + TestGBinderDataItem* next; + DATA_TYPE type; + union { + gint32 i32; + struct { + const void* buf; + gsize size; + } blob; + GBinderLocalObject* obj; + } data; + void (*destroy)(TestGBinderDataItem*); +}; + +struct test_gbinder_data { + guint32 refcount; + TestGBinderDataItem* items; +}; + +typedef struct test_gbinder_reader { + TestGBinderDataItem* item; +} TestGBinderReader; + +typedef struct test_gbinder_writer { + TestGBinderData* data; +} TestGBinderWriter; + +static inline TestGBinderReader* test_gbinder_reader_cast(GBinderReader* reader) + { return (TestGBinderReader*)reader; } + +static inline TestGBinderWriter* test_gbinder_writer_cast(GBinderWriter* writer) + { return (TestGBinderWriter*)writer; } + +static +void +test_gbinder_data_item_destroy_local_obj( + TestGBinderDataItem* item) +{ + g_assert_cmpint(item->type, == ,DATA_TYPE_LOCAL_OBJ); + gbinder_local_object_unref(item->data.obj); +} + +static +TestGBinderDataItem* +test_gbinder_data_item_new( + DATA_TYPE type) +{ + TestGBinderDataItem* item = g_new0(TestGBinderDataItem, 1); + + item->type = type; + return item; +} + +static +void +test_gbinder_data_item_free( + TestGBinderDataItem* item) +{ + if (item) { + test_gbinder_data_item_free(item->next); + if (item->destroy) { + item->destroy(item); + } + g_free(item); + } +} + +static +void +test_gbinder_data_free( + TestGBinderData* data) +{ + test_gbinder_data_item_free(data->items); + g_free(data); +} + +static +guint +test_gbinder_data_count_buffers( + TestGBinderData* data) +{ + TestGBinderDataItem* item; + guint n; + + for (n = 0, item = data->items; item; item = item->next) { + if (item->type == DATA_TYPE_BUFFER) { + n++; + } + } + return n; +} + +static +void +test_gbinder_data_append( + TestGBinderData* data, + TestGBinderDataItem* item) +{ + TestGBinderDataItem* last = data->items; + + if (last) { + while (last->next) { + last = last->next; + } + last->next = item; + } else { + data->items = item; + } +} + +/*==========================================================================* + * Internal API + *==========================================================================*/ + +TestGBinderData* +test_gbinder_data_new( + void) +{ + TestGBinderData* data = g_new0(TestGBinderData, 1); + + g_atomic_int_set(&data->refcount, 1); + return data; +} + +TestGBinderData* +test_gbinder_data_ref( + TestGBinderData* data) +{ + if (data) { + g_assert_cmpint(data->refcount, > ,0); + g_atomic_int_inc(&data->refcount); + } + return data; +} + +void +test_gbinder_data_unref( + TestGBinderData* data) +{ + if (data) { + g_assert_cmpint(data->refcount, > ,0); + if (g_atomic_int_dec_and_test(&data->refcount)) { + test_gbinder_data_free(data); + } + } +} + +void +test_gbinder_data_init_reader( + TestGBinderData* data, + GBinderReader* reader) +{ + memset(reader, 0, sizeof(*reader)); + if (data) { + test_gbinder_reader_cast(reader)->item = data->items; + } +} + +void +test_gbinder_data_init_writer( + TestGBinderData* data, + GBinderWriter* writer) +{ + memset(writer, 0, sizeof(*writer)); + if (data) { + test_gbinder_writer_cast(writer)->data = data; + } +} + +/*==========================================================================* + * libgbinder API + *==========================================================================*/ + +gboolean +gbinder_reader_read_uint32( + GBinderReader* reader, + guint32* value) +{ + TestGBinderReader* self = test_gbinder_reader_cast(reader); + TestGBinderDataItem* item = self->item; + + if (item && item->type == DATA_TYPE_INT32) { + if (value) { + *value = item->data.i32; + } + self->item = item->next; + return TRUE; + } + return FALSE; +} + +gboolean +gbinder_reader_read_int32( + GBinderReader* reader, + gint32* value) +{ + return gbinder_reader_read_uint32(reader, (guint32*)value); +} + +const void* +gbinder_reader_read_hidl_struct1( + GBinderReader* reader, + gsize size) +{ + TestGBinderReader* self = test_gbinder_reader_cast(reader); + TestGBinderDataItem* item = self->item; + + if (item && item->type == DATA_TYPE_BUFFER && + item->data.blob.size == size) { + self->item = item->next; + return item->data.blob.buf; + } + return NULL; +} + +GBinderRemoteObject* +gbinder_reader_read_object( + GBinderReader* reader) +{ + TestGBinderReader* self = test_gbinder_reader_cast(reader); + TestGBinderDataItem* item = self->item; + + if (item && item->type == DATA_TYPE_LOCAL_OBJ) { + self->item = item->next; + return test_gbinder_remote_object_new(item->data.obj); + } + return NULL; +} + +void +gbinder_writer_append_int32( + GBinderWriter* writer, + guint32 value) +{ + TestGBinderWriter* self = test_gbinder_writer_cast(writer); + TestGBinderDataItem* item = test_gbinder_data_item_new(DATA_TYPE_INT32); + + item->data.i32 = value; + test_gbinder_data_append(self->data, item); +} + +guint +gbinder_writer_append_buffer_object( + GBinderWriter* writer, + const void* buf, + gsize size) +{ + TestGBinderWriter* self = test_gbinder_writer_cast(writer); + TestGBinderDataItem* item = test_gbinder_data_item_new(DATA_TYPE_BUFFER); + const guint index = test_gbinder_data_count_buffers(self->data); + + item->data.blob.buf = buf; + item->data.blob.size = size; + test_gbinder_data_append(self->data, item); + return index; +} + +void +gbinder_writer_append_local_object( + GBinderWriter* writer, + GBinderLocalObject* obj) +{ + TestGBinderWriter* self = test_gbinder_writer_cast(writer); + TestGBinderDataItem* item = test_gbinder_data_item_new(DATA_TYPE_LOCAL_OBJ); + + item->data.obj = gbinder_local_object_ref(obj); + item->destroy = test_gbinder_data_item_destroy_local_obj; + test_gbinder_data_append(self->data, item); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_gbinder_remote_object.c b/unit/common/test_gbinder_remote_object.c new file mode 100644 index 0000000..c357a65 --- /dev/null +++ b/unit/common/test_gbinder_remote_object.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_gbinder.h" + +#include + +struct gbinder_remote_object { + GObject parent; + GBinderLocalObject* local; + gboolean dead; +}; + +typedef GObjectClass GBinderRemoteObjectClass; +G_DEFINE_TYPE(GBinderRemoteObject, gbinder_remote_object, G_TYPE_OBJECT) + +#define PARENT_CLASS gbinder_remote_object_parent_class +#define THIS_TYPE (gbinder_remote_object_get_type()) +#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,THIS_TYPE,GBinderRemoteObject) + +enum gbinder_remote_object_signal { + SIGNAL_DEATH, + SIGNAL_COUNT +}; + +#define SIGNAL_DEATH_NAME "death" + +static guint remote_object_signals[SIGNAL_COUNT] = { 0 }; + +static +void +gbinder_remote_object_init( + GBinderRemoteObject* self) +{ +} + +static +void +gbinder_remote_object_finalize( + GObject* object) +{ + GBinderRemoteObject* self = THIS(object); + + gbinder_local_object_unref(self->local); + G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); +} + +static +void +gbinder_remote_object_class_init( + GBinderRemoteObjectClass* klass) +{ + G_OBJECT_CLASS(klass)->finalize = gbinder_remote_object_finalize; + remote_object_signals[SIGNAL_DEATH] = + g_signal_new(SIGNAL_DEATH_NAME, G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +/*==========================================================================* + * Internal API + *==========================================================================*/ + +GBinderRemoteObject* +test_gbinder_remote_object_new( + GBinderLocalObject* local) +{ + GBinderRemoteObject* self = g_object_new(THIS_TYPE, NULL); + + g_assert(local); + self->local = gbinder_local_object_ref(local); + return self; +} + +void +test_gbinder_remote_object_kill( + GBinderRemoteObject* self) +{ + if (self && !self->dead) { + self->dead = TRUE; + g_signal_emit(self, remote_object_signals[SIGNAL_DEATH], 0); + } +} + +gboolean +test_gbinder_remote_object_dead( + GBinderRemoteObject* self) +{ + return !self || self->dead; +} + +GBinderLocalObject* +test_gbinder_remote_object_to_local( + GBinderRemoteObject* self) +{ + return self ? self->local : NULL; +} + +/*==========================================================================* + * libgbinder API + *==========================================================================*/ + +GBinderRemoteObject* +gbinder_remote_object_ref( + GBinderRemoteObject* self) +{ + if (self) { + g_object_ref(THIS(self)); + } + return self; +} + +void +gbinder_remote_object_unref( + GBinderRemoteObject* self) +{ + if (self) { + g_object_unref(THIS(self)); + } +} + +gulong +gbinder_remote_object_add_death_handler( + GBinderRemoteObject* self, + GBinderRemoteObjectNotifyFunc fn, + void* data) +{ + return (self && fn) ? g_signal_connect(self, SIGNAL_DEATH_NAME, + G_CALLBACK(fn), data) : 0; +} + +void +gbinder_remote_object_remove_handler( + GBinderRemoteObject* self, + gulong id) +{ + if (self && id) { + g_signal_handler_disconnect(self, id); + } +} + +/* + * Remote Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_gbinder_remote_reply.c b/unit/common/test_gbinder_remote_reply.c new file mode 100644 index 0000000..9fbd4da --- /dev/null +++ b/unit/common/test_gbinder_remote_reply.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_gbinder.h" + +struct gbinder_remote_reply { + guint32 refcount; + TestGBinderData* data; +}; + +static +void +test_gbinder_remote_reply_free( + GBinderRemoteReply* self) +{ + test_gbinder_data_unref(self->data); + g_free(self); +} + +/*==========================================================================* + * Internal API + *==========================================================================*/ + +GBinderRemoteReply* +test_gbinder_remote_reply_new( + GBinderLocalReply* reply) +{ + if (reply) { + GBinderRemoteReply* self = g_new0(GBinderRemoteReply, 1); + TestGBinderData* data = test_gbinder_local_reply_data(reply); + + g_atomic_int_set(&self->refcount, 1); + self->data = test_gbinder_data_ref(data); + return self; + } else { + return NULL; + } +} + +/*==========================================================================* + * libgbinder API + *==========================================================================*/ + +GBinderRemoteReply* +gbinder_remote_reply_ref( + GBinderRemoteReply* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + g_atomic_int_inc(&self->refcount); + } + return self; +} + +void +gbinder_remote_reply_unref( + GBinderRemoteReply* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + if (g_atomic_int_dec_and_test(&self->refcount)) { + test_gbinder_remote_reply_free(self); + } + } +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_gbinder_remote_request.c b/unit/common/test_gbinder_remote_request.c new file mode 100644 index 0000000..26b18b4 --- /dev/null +++ b/unit/common/test_gbinder_remote_request.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_gbinder.h" + +struct gbinder_remote_request { + guint32 refcount; + TestGBinderData* data; + char* iface; +}; + +static +void +test_gbinder_remote_request_free( + GBinderRemoteRequest* self) +{ + test_gbinder_data_unref(self->data); + g_free(self->iface); + g_free(self); +} + +/*==========================================================================* + * Internal API + *==========================================================================*/ + +GBinderRemoteRequest* +test_gbinder_remote_request_new( + GBinderLocalRequest* req) +{ + GBinderRemoteRequest* self = g_new0(GBinderRemoteRequest, 1); + + g_atomic_int_set(&self->refcount, 1); + self->data = test_gbinder_data_ref(test_gbinder_local_request_data(req)); + self->iface = g_strdup(test_gbinder_local_request_interface(req)); + return self; +} + +/*==========================================================================* + * libgbinder API + *==========================================================================*/ + +GBinderRemoteRequest* +gbinder_remote_request_ref( + GBinderRemoteRequest* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + g_atomic_int_inc(&self->refcount); + } + return self; +} + +void +gbinder_remote_request_unref( + GBinderRemoteRequest* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + if (g_atomic_int_dec_and_test(&self->refcount)) { + test_gbinder_remote_request_free(self); + } + } +} + +const char* +gbinder_remote_request_interface( + GBinderRemoteRequest* self) +{ + return self ? self->iface : NULL; +} + +void +gbinder_remote_request_init_reader( + GBinderRemoteRequest* self, + GBinderReader* reader) +{ + test_gbinder_data_init_reader(self->data, reader); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_gbinder_servicemanager.c b/unit/common/test_gbinder_servicemanager.c new file mode 100644 index 0000000..34f681e --- /dev/null +++ b/unit/common/test_gbinder_servicemanager.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_gbinder.h" + +#include +#include + +struct gbinder_servicemanager { + guint32 refcount; + GHashTable* services; + GUtilIdlePool* pool; + char* dev; +}; + +static GHashTable* test_servermanagers = NULL; + +static +void +test_gbinder_servicemanager_free( + GBinderServiceManager* self) +{ + /* Update the global table */ + g_assert(test_servermanagers); + g_assert(g_hash_table_contains(test_servermanagers, self->dev)); + g_hash_table_remove(test_servermanagers, self->dev); /* Frees self->dev */ + if (g_hash_table_size(test_servermanagers) == 0) { + g_hash_table_unref(test_servermanagers); + test_servermanagers = NULL; + } + + gutil_idle_pool_destroy(self->pool); + g_hash_table_destroy(self->services); + g_free(self); +} + +/*==========================================================================* + * Internal API + *==========================================================================*/ + +GBinderRemoteObject* +test_gbinder_servicemanager_new_service( + GBinderServiceManager* self, + const char* name, + GBinderLocalObject* local) +{ + GBinderRemoteObject* remote = test_gbinder_remote_object_new(local); + + g_hash_table_replace(self->services, g_strdup(name), remote); + return gbinder_remote_object_ref(remote); +} + +/*==========================================================================* + * libgbinder API + *==========================================================================*/ + +GBinderServiceManager* +gbinder_servicemanager_new( + const char* dev) +{ + GBinderServiceManager* self = NULL; + + g_assert(dev && dev[0]); + if (test_servermanagers) { + self = g_hash_table_lookup(test_servermanagers, dev); + } + + if (self) { + gbinder_servicemanager_ref(self); + } else { + self = g_new0(GBinderServiceManager, 1); + g_atomic_int_set(&self->refcount, 1); + self->pool = gutil_idle_pool_new(); + self->dev = g_strdup(dev); /* Owned by test_servermanagers */ + self->services = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify) gbinder_remote_object_unref); + + /* Update the global table */ + if (!test_servermanagers) { + test_servermanagers = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + } + g_hash_table_replace(test_servermanagers, self->dev, self); + } + return self; +} + +GBinderServiceManager* +gbinder_servicemanager_ref( + GBinderServiceManager* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + g_atomic_int_inc(&self->refcount); + } + return self; +} + +void +gbinder_servicemanager_unref( + GBinderServiceManager* self) +{ + if (self) { + g_assert_cmpint(self->refcount, > ,0); + if (g_atomic_int_dec_and_test(&self->refcount)) { + test_gbinder_servicemanager_free(self); + } + } +} + +GBinderRemoteObject* /* autoreleased */ +gbinder_servicemanager_get_service_sync( + GBinderServiceManager* self, + const char* name, + int* status) +{ + if (self && name) { + GBinderRemoteObject* obj = g_hash_table_lookup(self->services, name); + + if (obj) { + gutil_idle_pool_add(self->pool, gbinder_remote_object_ref(obj), + (GDestroyNotify) gbinder_remote_object_unref); + return obj; + } else { + GDEBUG("Name %s not found", name); + } + } + return NULL; +} + +GBinderLocalObject* +gbinder_servicemanager_new_local_object2( + GBinderServiceManager* self, + const char* const* ifaces, + GBinderLocalTransactFunc fn, + void* user_data) +{ + return self ? test_gbinder_local_object_new(ifaces, fn, user_data) : NULL; +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/common/test_main.c b/unit/common/test_main.c new file mode 100644 index 0000000..0d8d89d --- /dev/null +++ b/unit/common/test_main.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2018-2021 Jolla Ltd. + * Copyright (C) 2018-2021 Slava Monich + * + * You may use this file under the terms of BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test_common.h" + +#include + +typedef struct test_quit_later_data{ + GMainLoop* loop; + guint n; +} TestQuitLaterData; + +typedef struct test_context_data{ + GMainLoop* loop; + GTestFunc func; +} TestContextData; + +static +gboolean +test_timeout_expired( + gpointer data) +{ + g_assert(!"TIMEOUT"); + return G_SOURCE_REMOVE; +} + +static +void +test_quit_later_n_free( + gpointer user_data) +{ + TestQuitLaterData* data = user_data; + + g_main_loop_unref(data->loop); + g_free(data); +} + +static +gboolean +test_quit_later_n_func( + gpointer user_data) +{ + TestQuitLaterData* data = user_data; + + if (data->n > 0) { + data->n--; + return G_SOURCE_CONTINUE; + } else { + g_main_loop_quit(data->loop); + return G_SOURCE_REMOVE; + } +} + +void +test_quit_later_n( + GMainLoop* loop, + guint n) +{ + TestQuitLaterData* data = g_new0(TestQuitLaterData, 1); + + data->loop = g_main_loop_ref(loop); + data->n = n; + g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, test_quit_later_n_func, data, + test_quit_later_n_free); +} + +static +gboolean +test_quit_later_cb( + gpointer data) +{ + g_main_loop_quit((GMainLoop*)data); + return G_SOURCE_REMOVE; +} + +void +test_quit_later( + GMainLoop* loop) +{ + g_idle_add(test_quit_later_cb, loop); +} + +void +test_run( + const TestOpt* opt, + GMainLoop* loop) +{ + if (opt->flags & TEST_FLAG_DEBUG) { + g_main_loop_run(loop); + } else { + const guint timeout_id = g_timeout_add_seconds(TEST_TIMEOUT_SEC, + test_timeout_expired, NULL); + g_main_loop_run(loop); + g_source_remove(timeout_id); + } +} + +void +test_init( + TestOpt* opt, + int argc, + char* argv[]) +{ + const char* sep1; + const char* sep2; + int i; + + memset(opt, 0, sizeof(*opt)); + for (i=1; iflags |= TEST_FLAG_DEBUG; + } else if (!strcmp(arg, "-v")) { + GTestConfig* config = (GTestConfig*)g_test_config_vars; + config->test_verbose = TRUE; + } else { + GWARN("Unsupported command line option %s", arg); + } + } + + /* Setup logging */ + sep1 = strrchr(argv[0], '/'); + sep2 = strrchr(argv[0], '\\'); + gutil_log_default.name = (sep1 && sep2) ? (MAX(sep1, sep2) + 1) : + sep1 ? (sep1 + 1) : sep2 ? (sep2 + 1) : argv[0]; + gutil_log_default.level = g_test_verbose() ? + GLOG_LEVEL_VERBOSE : GLOG_LEVEL_NONE; + gutil_log_timestamp = FALSE; +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/coverage/run b/unit/coverage/run new file mode 100755 index 0000000..4e69d46 --- /dev/null +++ b/unit/coverage/run @@ -0,0 +1,54 @@ +#!/bin/bash +# +# This script requires lcov, dirname +# + +TESTS="\ +unit_instance \ +unit_registry \ +unit_util" + +function err() { + echo "*** ERROR!" $1 + exit 1 +} + +# Check the required tools +which lcov >> /dev/null || err "Please install lcov" +which dirname >> /dev/null || err "Please install dirname" + +# LCOV 1.10 has branch coverage disabled per default +# Previous versions didn't have the --rc option +if [ ! -z "$(lcov --help | grep ' --rc ')" ] ; then + LCOV_OPT="--rc lcov_branch_coverage=1" + GENHTML_OPT="--branch-coverage" +fi + +pushd `dirname $0` > /dev/null +COV_DIR="$PWD" +pushd .. > /dev/null +TEST_DIR="$PWD" +pushd .. > /dev/null +TOP_DIR="$PWD" +popd > /dev/null +popd > /dev/null +popd > /dev/null + +make -C "$TOP_DIR" clean +for t in $TESTS ; do + pushd "$TEST_DIR/$t" + make -C "$TEST_DIR/$t" clean coverage || exit 1 + build/coverage/$t || exit 1 + popd +done + +# Sometimes you need this, sometimes that :S +BASE_DIR="$TOP_DIR" +#BASE_DIR="$TOP_DIR/src" + +FULL_COV="$COV_DIR/full.gcov" +LIB_COV="$COV_DIR/lib.gcov" +rm -f "$FULL_COV" "$LIB_COV" +lcov $LCOV_OPT -c -d "$TOP_DIR/build/coverage" -b "$BASE_DIR" -o "$FULL_COV" || exit 1 +lcov $LCOV_OPT -e "$FULL_COV" "$BASE_DIR/*" -o "$LIB_COV" || exit 1 +genhtml $GENHTML_OPT "$LIB_COV" -t "libgbinder-radio" --output-directory "$COV_DIR/report" || exit 1 diff --git a/unit/unit_instance/Makefile b/unit/unit_instance/Makefile new file mode 100644 index 0000000..e57cbff --- /dev/null +++ b/unit/unit_instance/Makefile @@ -0,0 +1,5 @@ +# -*- Mode: makefile-gmake -*- + +EXE = unit_instance + +include ../common/Makefile diff --git a/unit/unit_instance/unit_instance.c b/unit/unit_instance/unit_instance.c new file mode 100644 index 0000000..a1bbfe5 --- /dev/null +++ b/unit/unit_instance/unit_instance.c @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_common.h" +#include "test_gbinder.h" + +#include "radio_instance.h" +#include "radio_util.h" + +#include +#include + +#define DEFAULT_INTERFACE RADIO_INTERFACE_1_0 +#define DEV GBINDER_DEFAULT_BINDER + +#define UNKNOWN_VALUE (0x7fffffff) +#define UNKNOWN_VALUE_STR "2147483647" /* 0x7fffffff */ +#define UNKNOWN_REQ ((RADIO_REQ)UNKNOWN_VALUE) +#define UNKNOWN_REQ_STR UNKNOWN_VALUE_STR +#define UNKNOWN_IND ((RADIO_IND)UNKNOWN_VALUE) +#define UNKNOWN_IND_STR UNKNOWN_VALUE_STR +#define UNKNOWN_RESP ((RADIO_RESP)UNKNOWN_VALUE) +#define UNKNOWN_RESP_STR UNKNOWN_VALUE_STR +#define INVALID_IND_TYPE ((RADIO_IND_TYPE)UNKNOWN_VALUE) + +RadioInstance* +radio_instance_get_with_interface( + const char* dev, + const char* name, + RADIO_INTERFACE version); /* Deprecated and removed from the .h file */ + +gboolean +radio_instance_is_dead( + RadioInstance* self); /* No sure why this one is missing */ + +typedef struct test_radio_service { + GBinderLocalObject* obj; + GBinderRemoteObject* resp_obj; + GBinderRemoteObject* ind_obj; + GHashTable* req_count; +} TestRadioService; + +static TestOpt test_opt; + +static const GBinderClientIfaceInfo radio_ind_iface_info[] = { + {RADIO_INDICATION_1_4, RADIO_1_4_IND_LAST }, + {RADIO_INDICATION_1_3, RADIO_1_3_IND_LAST }, + {RADIO_INDICATION_1_2, RADIO_1_2_IND_LAST }, + {RADIO_INDICATION_1_1, RADIO_1_1_IND_LAST }, + {RADIO_INDICATION_1_0, RADIO_1_0_IND_LAST } +}; + +static const GBinderClientIfaceInfo radio_resp_iface_info[] = { + {RADIO_RESPONSE_1_4, RADIO_1_4_RESP_LAST }, + {RADIO_RESPONSE_1_3, RADIO_1_3_RESP_LAST }, + {RADIO_RESPONSE_1_2, RADIO_1_2_RESP_LAST }, + {RADIO_RESPONSE_1_1, RADIO_1_1_RESP_LAST }, + {RADIO_RESPONSE_1_0, RADIO_1_0_RESP_LAST } +}; + +static const char* const radio_req_ifaces[] = { + RADIO_1_4, + RADIO_1_3, + RADIO_1_2, + RADIO_1_1, + RADIO_1_0, + NULL +}; + +static +gboolean +test_response_not_handled( + RadioInstance* radio, + RADIO_RESP code, + const RadioResponseInfo* info, + const GBinderReader* reader, + gpointer user_data) +{ + g_assert_not_reached(); + return FALSE; +} + +static +void +test_response_not_observed( + RadioInstance* radio, + RADIO_RESP code, + const RadioResponseInfo* info, + const GBinderReader* reader, + gpointer user_data) +{ + g_assert_not_reached(); +} + +/*==========================================================================* + * Test IRadio service + *==========================================================================*/ + +static +int +test_service_req_count( + TestRadioService* service, + RADIO_REQ req) +{ + return GPOINTER_TO_INT(g_hash_table_lookup(service->req_count, + GINT_TO_POINTER(req))); +} + +static +GBinderLocalReply* +test_service_txproc( + GBinderLocalObject* obj, + GBinderRemoteRequest* req, + guint code, + guint flags, + int* status, + void* user_data) +{ + TestRadioService* service = user_data; + const char* iface = gbinder_remote_request_interface(req); + + if (gutil_strv_contains((const GStrV*)radio_req_ifaces, iface)) { + const int count = test_service_req_count(service, code) + 1; + GBinderReader reader; + + GDEBUG("%s %s %d", iface, radio_req_name(code), count); + g_hash_table_insert(service->req_count, GINT_TO_POINTER(code), + GINT_TO_POINTER(count)); + + gbinder_remote_request_init_reader(req, &reader); + switch (code) { + case RADIO_REQ_SET_RESPONSE_FUNCTIONS: + gbinder_remote_object_unref(service->resp_obj); + gbinder_remote_object_unref(service->ind_obj); + service->resp_obj = gbinder_reader_read_object(&reader); + service->ind_obj = gbinder_reader_read_object(&reader); + g_assert(service->resp_obj); + g_assert(service->ind_obj); + break; + } + *status = GBINDER_STATUS_OK; + return NULL; + } else { + GDEBUG("%s %u", iface, code); + *status = GBINDER_STATUS_FAILED; + return NULL; + } +} + +static +void +test_service_init( + TestRadioService* service) +{ + memset(service, 0, sizeof(*service)); + service->obj = test_gbinder_local_object_new(NULL, + test_service_txproc, service); + service->req_count = g_hash_table_new(g_direct_hash, g_direct_equal); +} + +static +void +test_service_cleanup( + TestRadioService* service) +{ + g_hash_table_destroy(service->req_count); + gbinder_remote_object_unref(service->resp_obj); + gbinder_remote_object_unref(service->ind_obj); + gbinder_local_object_unref(service->obj); + memset(service, 0, sizeof(*service)); +} + +/*==========================================================================* + * null + *==========================================================================*/ + +static +void +test_null( + void) +{ + radio_instance_set_enabled(NULL, FALSE); + radio_instance_remove_handler(NULL, 0); + radio_instance_remove_handlers(NULL, NULL, 0); + radio_instance_unref(NULL); + + g_assert(radio_instance_is_dead(NULL)); + + g_assert(!radio_instance_get(NULL, NULL)); + g_assert(!radio_instance_get("", NULL)); + g_assert(!radio_instance_get("/dev/binder", NULL)); + g_assert(!radio_instance_get("/dev/binder", "")); + g_assert(!radio_instance_get_with_interface("", "", DEFAULT_INTERFACE)); + g_assert(!radio_instance_get_with_version("foo", "bar", DEFAULT_INTERFACE)); + g_assert(!radio_instance_new(NULL, NULL)); + g_assert(!radio_instance_new_with_modem_and_slot(NULL, NULL, NULL, 0)); + g_assert(!radio_instance_new_with_version(NULL, NULL, DEFAULT_INTERFACE)); + g_assert(!radio_instance_new_with_version(NULL, "", DEFAULT_INTERFACE)); + g_assert(!radio_instance_new_request(NULL, 0)); + g_assert(!radio_instance_ack(NULL)); + g_assert(!radio_instance_ref(NULL)); + g_assert(!radio_instance_send_request_sync(NULL, 0, NULL)); + g_assert(!radio_instance_add_indication_handler(NULL, 0, NULL, NULL)); + g_assert(!radio_instance_add_indication_observer(NULL, 0, NULL, NULL)); + g_assert(!radio_instance_add_response_handler(NULL, 0, NULL, NULL)); + g_assert(!radio_instance_add_response_observer(NULL, 0, NULL, NULL)); + g_assert(!radio_instance_add_ack_handler(NULL, NULL, NULL)); + g_assert(!radio_instance_add_death_handler(NULL, NULL, NULL)); + g_assert(!radio_instance_add_enabled_handler(NULL, NULL, NULL)); + g_assert(!radio_instance_req_name(NULL, UNKNOWN_REQ)); + g_assert(!radio_instance_resp_name(NULL, UNKNOWN_RESP)); + g_assert(!radio_instance_ind_name(NULL, UNKNOWN_IND)); +} + +/*==========================================================================* + * basic + *==========================================================================*/ + +static +void +test_basic( + void) +{ + GBinderServiceManager* sm = gbinder_servicemanager_new(DEV); + GBinderRemoteObject* remote; + RadioInstance* radio; + RadioInstance* const* radios; + TestRadioService service; + const RADIO_INTERFACE version = RADIO_INTERFACE_1_4; + const char* slot = "slot1"; + const char* fqname = RADIO_1_0 "/slot1"; + + /* This fails because there's no radio service */ + g_assert(!radio_instance_new_with_version(DEV, slot, DEFAULT_INTERFACE)); + g_assert(!radio_instance_get_all()); + + /* Register the service to create an instance */ + test_service_init(&service); + remote = test_gbinder_servicemanager_new_service(sm, fqname, service.obj); + radio = radio_instance_new_with_version(DEV, slot, version); + g_assert(radio); + g_assert(service.ind_obj); + g_assert(service.resp_obj); + + /* The second call returns new reference to the same instance */ + g_assert(radio == radio_instance_new_with_version(DEV, slot, version)); + radio_instance_unref(radio); + + /* The one we have created must still be there */ + g_assert(radio == radio_instance_get_with_version(DEV, slot, version)); + + /* NULL callbacks are ignored */ + g_assert(!radio_instance_add_indication_handler(radio, 0, NULL, NULL)); + g_assert(!radio_instance_add_indication_observer(radio, 0, NULL, NULL)); + g_assert(!radio_instance_add_response_handler(radio, 0, NULL, NULL)); + g_assert(!radio_instance_add_response_observer(radio, 0, NULL, NULL)); + g_assert(!radio_instance_add_ack_handler(radio, NULL, NULL)); + g_assert(!radio_instance_add_death_handler(radio, NULL, NULL)); + g_assert(!radio_instance_add_enabled_handler(radio, NULL, NULL)); + + /* Formatting unknown codes (RadioInstance owns the string) */ + g_assert_cmpstr(radio_instance_req_name(radio, UNKNOWN_REQ), == , + UNKNOWN_REQ_STR); + g_assert_cmpstr(radio_instance_resp_name(radio, UNKNOWN_RESP), == , + UNKNOWN_RESP_STR); + g_assert_cmpstr(radio_instance_ind_name(radio, UNKNOWN_IND), == , + UNKNOWN_IND_STR); + g_assert_cmpstr(radio_instance_req_name(radio, + RADIO_REQ_DIAL), == ,"dial"); + g_assert_cmpstr(radio_instance_resp_name(radio, + RADIO_RESP_DIAL), == ,"dialResponse"); + g_assert_cmpstr(radio_instance_ind_name(radio, + RADIO_IND_MODEM_RESET), == ,"modemReset"); + + /* The entire list consists of that one instance */ + radios = radio_instance_get_all(); + g_assert(radios); + g_assert(radios[0] == radio); + g_assert(!radios[1]); + + radio_instance_unref(radio); + test_service_cleanup(&service); + gbinder_remote_object_unref(remote); + gbinder_servicemanager_unref(sm); +} + +/*==========================================================================* + * ind + *==========================================================================*/ + +static +gboolean +test_ind_handle( + RadioInstance* radio, + RADIO_IND code, + RADIO_IND_TYPE type, + const GBinderReader* reader, + gpointer user_data) +{ + int* expected = user_data; + + g_assert_cmpint(code, == ,*expected); + *expected = RADIO_IND_NONE; + radio_instance_ack(radio); + return TRUE; +} + +static +void +test_ind_observe( + RadioInstance* radio, + RADIO_IND code, + RADIO_IND_TYPE type, + const GBinderReader* reader, + gpointer user_data) +{ + int* expected = user_data; + + g_assert_cmpint(code, == ,*expected); + *expected = RADIO_IND_NONE; +} + +static +void +test_ind( + void) +{ + GBinderServiceManager* sm = gbinder_servicemanager_new(DEV); + GBinderRemoteObject* remote; + RadioInstance* radio; + TestRadioService service; + GBinderClient* ind; + GBinderLocalRequest* req; + const RADIO_INTERFACE version = RADIO_INTERFACE_1_4; + const char* slot = "slot1"; + const char* fqname = RADIO_1_0 "/slot1"; + int code[2]; + ulong id[2]; + + /* Register the service to create an instance */ + test_service_init(&service); + remote = test_gbinder_servicemanager_new_service(sm, fqname, service.obj); + radio = radio_instance_new_with_version(DEV, slot, version); + g_assert(radio); + + /* Issue invalid indication (no type) */ + code[0] = code[1] = RADIO_IND_RIL_CONNECTED; + id[0] = radio_instance_add_indication_handler(radio, RADIO_IND_RIL_CONNECTED, + test_ind_handle, code + 0); + id[1] = radio_instance_add_indication_observer(radio, RADIO_IND_ANY, + test_ind_observe, code + 1); + g_assert(service.ind_obj); + ind = gbinder_client_new2(service.ind_obj, + TEST_ARRAY_AND_COUNT(radio_ind_iface_info)); + req = gbinder_client_new_request2(ind, RADIO_IND_RIL_CONNECTED); + g_assert_cmpint(gbinder_client_transact_sync_oneway(ind, + RADIO_IND_RIL_CONNECTED, req), == ,GBINDER_STATUS_FAILED); + + /* No signals issued and no acks sent */ + g_assert_cmpint(code[0], == ,RADIO_IND_RIL_CONNECTED); + g_assert_cmpint(code[1], == ,RADIO_IND_RIL_CONNECTED); + g_assert_cmpint(test_service_req_count(&service, + RADIO_REQ_RESPONSE_ACKNOWLEDGEMENT), == ,0); + + /* Another invalid indication (invalid type) */ + gbinder_local_request_append_int32(req, INVALID_IND_TYPE); + g_assert_cmpint(gbinder_client_transact_sync_oneway(ind, + RADIO_IND_RIL_CONNECTED, req), == ,GBINDER_STATUS_FAILED); + + /* No signals issued and no acks sent */ + g_assert_cmpint(code[0], == ,RADIO_IND_RIL_CONNECTED); + g_assert_cmpint(code[0], == ,RADIO_IND_RIL_CONNECTED); + g_assert_cmpint(test_service_req_count(&service, + RADIO_REQ_RESPONSE_ACKNOWLEDGEMENT), == ,0); + + /* Build a valid request and try again */ + gbinder_local_request_unref(req); + req = gbinder_client_new_request2(ind, RADIO_IND_RIL_CONNECTED); + gbinder_local_request_append_int32(req, RADIO_IND_ACK_EXP); + g_assert_cmpint(gbinder_client_transact_sync_oneway(ind, + RADIO_IND_RIL_CONNECTED, req), == ,GBINDER_STATUS_OK); + + /* This time both handler and observer are notified, ack is sent */ + g_assert_cmpint(code[0], == ,RADIO_IND_NONE); + g_assert_cmpint(code[1], == ,RADIO_IND_NONE); + g_assert_cmpint(test_service_req_count(&service, + RADIO_REQ_RESPONSE_ACKNOWLEDGEMENT), == ,1); + + /* Now issue callStateChanged but only observe it (don't handle) */ + radio_instance_remove_handlers(radio, id, 1); + code[1] = RADIO_IND_CALL_STATE_CHANGED; + g_assert_cmpint(gbinder_client_transact_sync_oneway(ind, + RADIO_IND_CALL_STATE_CHANGED, req), == ,GBINDER_STATUS_OK); + g_assert_cmpint(code[1], == ,RADIO_IND_NONE); + g_assert_cmpint(test_service_req_count(&service, + RADIO_REQ_RESPONSE_ACKNOWLEDGEMENT), == ,2); /* Unhandled but acked */ + + /* Same thing but without ack */ + gbinder_local_request_unref(req); + req = gbinder_client_new_request2(ind, RADIO_IND_CALL_STATE_CHANGED); + gbinder_local_request_append_int32(req, RADIO_IND_UNSOLICITED); + code[1] = RADIO_IND_CALL_STATE_CHANGED; + g_assert_cmpint(gbinder_client_transact_sync_oneway(ind, + RADIO_IND_CALL_STATE_CHANGED, req), == ,GBINDER_STATUS_OK); + g_assert_cmpint(code[1], == ,RADIO_IND_NONE); + g_assert_cmpint(test_service_req_count(&service, + RADIO_REQ_RESPONSE_ACKNOWLEDGEMENT), == ,2); /* Still 2 (not acked) */ + + /* Unsupported interface */ + gbinder_local_request_unref(req); + req = test_gbinder_local_request_new("foo"); + g_assert_cmpint(gbinder_client_transact_sync_oneway(ind, + UNKNOWN_IND, req), == ,GBINDER_STATUS_FAILED); + gbinder_local_request_unref(req); + gbinder_client_unref(ind); + + radio_instance_remove_all_handlers(radio, id); + radio_instance_unref(radio); + test_service_cleanup(&service); + gbinder_remote_object_unref(remote); + gbinder_servicemanager_unref(sm); +} + +/*==========================================================================* + * req + *==========================================================================*/ + +#define TEST_REQ RADIO_REQ_DIAL + +static +void +test_req( + void) +{ + const char* slot = "slot1"; + const char* fqname = RADIO_1_0 "/slot1"; + TestRadioService service; + GBinderServiceManager* sm = gbinder_servicemanager_new(DEV); + GBinderRemoteObject* remote; + GBinderLocalRequest* req; + RadioInstance* radio; + + test_service_init(&service); + remote = test_gbinder_servicemanager_new_service(sm, fqname, service.obj); + radio = radio_instance_new_with_version(DEV, slot, RADIO_INTERFACE_1_4); + req = radio_instance_new_request(radio, TEST_REQ); + gbinder_local_request_append_int32(req, 123); + g_assert(radio_instance_send_request_sync(radio, TEST_REQ, req)); + g_assert_cmpint(test_service_req_count(&service, TEST_REQ), == ,1); + + radio_instance_unref(radio); + test_service_cleanup(&service); + gbinder_local_request_unref(req); + gbinder_remote_object_unref(remote); + gbinder_servicemanager_unref(sm); +} + +/*==========================================================================* + * resp + *==========================================================================*/ + +#define TEST_RESP RADIO_RESP_DIAL + +static +void +test_resp_observe( + RadioInstance* radio, + RADIO_RESP code, + const RadioResponseInfo* info, + const GBinderReader* reader, + gpointer user_data) +{ + guint* expected = user_data; + + GDEBUG("Observing resp %u", code); + g_assert_cmpuint(info->serial, == ,*expected); + *expected = 0; +} + +static +gboolean +test_resp_handle( + RadioInstance* radio, + RADIO_RESP code, + const RadioResponseInfo* info, + const GBinderReader* reader, + gpointer user_data) +{ + guint* expected = user_data; + + GDEBUG("Handling resp %u", code); + g_assert_cmpuint(info->serial, == ,*expected); + *expected = 0; + if (info->type == RADIO_RESP_SOLICITED_ACK_EXP) { + radio_instance_ack(radio); + } + return TRUE; +} + +static +void +test_resp( + void) +{ + const char* slot = "slot2"; + const char* fqname = RADIO_1_0 "/slot2"; + TestRadioService service; + GBinderServiceManager* sm = gbinder_servicemanager_new(DEV); + GBinderRemoteObject* remote; + GBinderLocalRequest* req; + GBinderClient* resp; + RadioInstance* radio; + RadioResponseInfo info; + GBinderWriter writer; + guint handle_serial, observe_serial; + gulong id[2]; + + test_service_init(&service); + + memset(&info, 0, sizeof(info)); + info.type = RADIO_RESP_SOLICITED_ACK_EXP; + handle_serial = observe_serial = info.serial = 123; + + remote = test_gbinder_servicemanager_new_service(sm, fqname, service.obj); + radio = radio_instance_new_with_version(DEV, slot, RADIO_INTERFACE_1_4); + id[0] = radio_instance_add_response_handler(radio, TEST_RESP, + test_resp_handle, &handle_serial); + id[1] = radio_instance_add_response_observer(radio, TEST_RESP, + test_resp_observe, &observe_serial); + + g_assert(service.resp_obj); + resp = gbinder_client_new2(service.resp_obj, + TEST_ARRAY_AND_COUNT(radio_resp_iface_info)); + + /* Submit broken respose first (without info) */ + req = gbinder_client_new_request2(resp, TEST_RESP); + g_assert_cmpint(gbinder_client_transact_sync_oneway(resp, TEST_RESP, req), + == ,GBINDER_STATUS_OK); + g_assert_cmpint(test_service_req_count(&service, + RADIO_REQ_RESPONSE_ACKNOWLEDGEMENT), == ,0); + + /* Add the info and try again */ + gbinder_local_request_init_writer(req, &writer); + gbinder_writer_append_buffer_object(&writer, &info, sizeof(info)); + g_assert_cmpint(gbinder_client_transact_sync_oneway(resp, TEST_RESP, req), + == ,GBINDER_STATUS_OK); + g_assert_cmpint(test_service_req_count(&service, + RADIO_REQ_RESPONSE_ACKNOWLEDGEMENT), == ,1); + g_assert(!handle_serial); /* Cleared by the handler */ + g_assert(!observe_serial); /* Cleared by the observer */ + + /* Remove the handler and check auto-ack */ + radio_instance_remove_handlers(radio, id, 1); + handle_serial = observe_serial = info.serial = 124; + g_assert_cmpint(gbinder_client_transact_sync_oneway(resp, TEST_RESP, req), + == ,GBINDER_STATUS_OK); + g_assert_cmpint(test_service_req_count(&service, + RADIO_REQ_RESPONSE_ACKNOWLEDGEMENT), == ,2); /* Acked */ + g_assert_cmpuint(handle_serial, == ,info.serial); /* No handler */ + g_assert(!observe_serial); /* Cleared by the observer */ + + /* RADIO_RESP_SOLICITED won't be acked */ + info.type = RADIO_RESP_SOLICITED; + handle_serial = observe_serial = info.serial = 125; + g_assert_cmpint(gbinder_client_transact_sync_oneway(resp, TEST_RESP, req), + == ,GBINDER_STATUS_OK); + g_assert_cmpint(test_service_req_count(&service, + RADIO_REQ_RESPONSE_ACKNOWLEDGEMENT), == ,2); /* Not acked */ + g_assert_cmpuint(handle_serial, == ,info.serial); /* No handler */ + g_assert(!observe_serial); /* Cleared by the observer */ + + /* Unsupported interface */ + gbinder_local_request_unref(req); + req = test_gbinder_local_request_new("foo"); + g_assert_cmpint(gbinder_client_transact_sync_oneway(resp, TEST_RESP, req), + == ,GBINDER_STATUS_FAILED); + g_assert_cmpint(test_service_req_count(&service, + RADIO_REQ_RESPONSE_ACKNOWLEDGEMENT), == ,2); /* Didn't change */ + + gbinder_local_request_unref(req); + gbinder_client_unref(resp); + + radio_instance_remove_all_handlers(radio, id); + radio_instance_unref(radio); + test_service_cleanup(&service); + gbinder_remote_object_unref(remote); + gbinder_servicemanager_unref(sm); +} + +/*==========================================================================* + * ack + *==========================================================================*/ + +static +void +test_ack_cb( + RadioInstance* radio, + guint32 serial, + gpointer user_data) +{ + guint* expected = user_data; + + GDEBUG("ack %u", serial); + g_assert_cmpuint(serial, == ,*expected); + *expected = 0; +} + +static +void +test_ack( + void) +{ + const char* slot = "slot1"; + const char* fqname = RADIO_1_0 "/slot1"; + TestRadioService service; + GBinderServiceManager* sm = gbinder_servicemanager_new(DEV); + GBinderRemoteObject* remote; + GBinderLocalRequest* req; + GBinderClient* resp; + RadioInstance* radio; + guint serial = 123; + gulong id[3]; + + test_service_init(&service); + remote = test_gbinder_servicemanager_new_service(sm, fqname, service.obj); + radio = radio_instance_new_with_version(DEV, slot, RADIO_INTERFACE_1_4); + id[0] = radio_instance_add_ack_handler(radio, test_ack_cb, &serial); + id[1] = radio_instance_add_response_handler(radio, RADIO_RESP_ANY, + test_response_not_handled, NULL); + id[2] = radio_instance_add_response_observer(radio, + RADIO_RESP_ACKNOWLEDGE_REQUEST, test_response_not_observed, NULL); + + g_assert(service.resp_obj); + resp = gbinder_client_new2(service.resp_obj, + TEST_ARRAY_AND_COUNT(radio_resp_iface_info)); + + /* Submit broken ack first (without serial) */ + req = gbinder_client_new_request2(resp, RADIO_RESP_ACKNOWLEDGE_REQUEST); + g_assert_cmpint(gbinder_client_transact_sync_oneway(resp, + RADIO_RESP_ACKNOWLEDGE_REQUEST, req), == ,GBINDER_STATUS_OK); + g_assert(serial); /* Transaction succeeds but handler is not called */ + + /* Add the serial and try again */ + gbinder_local_request_append_int32(req, serial); + g_assert_cmpint(gbinder_client_transact_sync_oneway(resp, + RADIO_RESP_ACKNOWLEDGE_REQUEST, req), == ,GBINDER_STATUS_OK); + g_assert(!serial); /* Cleared by the handler */ + gbinder_local_request_unref(req); + gbinder_client_unref(resp); + + radio_instance_remove_all_handlers(radio, id); + radio_instance_unref(radio); + test_service_cleanup(&service); + gbinder_remote_object_unref(remote); + gbinder_servicemanager_unref(sm); +} + +/*==========================================================================* + * enabled + *==========================================================================*/ + +static +void +test_enabled_cb( + RadioInstance* radio, + gpointer user_data) +{ + GDEBUG("%sabled", radio->enabled ? "En" : "Dis"); + (*((int*)user_data))++; +} + +static +void +test_enabled( + void) +{ + const char* slot = "slot1"; + const char* fqname = RADIO_1_0 "/slot1"; + TestRadioService service; + GBinderServiceManager* sm = gbinder_servicemanager_new(DEV); + GBinderRemoteObject* remote; + RadioInstance* radio; + int n = 0; + gulong id; + + test_service_init(&service); + remote = test_gbinder_servicemanager_new_service(sm, fqname, service.obj); + radio = radio_instance_new_with_version(DEV, slot, RADIO_INTERFACE_1_4); + id = radio_instance_add_enabled_handler(radio, test_enabled_cb, &n); + + g_assert(id); + g_assert(!radio->enabled); + radio_instance_set_enabled(radio, TRUE); + g_assert_cmpint(n, == ,1); + g_assert(radio->enabled); + + radio_instance_set_enabled(radio, TRUE); + g_assert(radio->enabled); + g_assert_cmpint(n, == ,1); /* Nothing changed */ + + radio_instance_set_enabled(radio, FALSE); + g_assert_cmpint(n, == ,2); + g_assert(!radio->enabled); + + radio_instance_remove_handler(radio, id); + radio_instance_unref(radio); + test_service_cleanup(&service); + gbinder_remote_object_unref(remote); + gbinder_servicemanager_unref(sm); +} + +/*==========================================================================* + * death + *==========================================================================*/ + +static +void +test_death_cb( + RadioInstance* radio, + gpointer user_data) +{ + GDEBUG("Boom"); + (*((int*)user_data))++; +} + +static +void +test_death( + void) +{ + const char* slot = "slot1"; + const char* fqname = RADIO_1_0 "/slot1"; + TestRadioService service; + GBinderServiceManager* sm = gbinder_servicemanager_new(DEV); + GBinderRemoteObject* remote; + RadioInstance* radio; + int n = 0; + gulong id; + + test_service_init(&service); + remote = test_gbinder_servicemanager_new_service(sm, fqname, service.obj); + radio = radio_instance_new_with_version(DEV, slot, RADIO_INTERFACE_1_4); + id = radio_instance_add_death_handler(radio, test_death_cb, &n); + + g_assert(id); + g_assert(!radio_instance_is_dead(radio)); + test_gbinder_remote_object_kill(remote); + g_assert_cmpint(n, == ,1); + g_assert(radio_instance_is_dead(radio)); + + radio_instance_remove_handler(radio, 0); /* no effect */ + radio_instance_remove_handler(radio, id); + radio_instance_unref(radio); + test_service_cleanup(&service); + gbinder_remote_object_unref(remote); + gbinder_servicemanager_unref(sm); +} + +/*==========================================================================* + * Common + *==========================================================================*/ + +#define TEST_PREFIX "/instance/" +#define TEST_(t) TEST_PREFIX t + +int main(int argc, char* argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func(TEST_("null"), test_null); + g_test_add_func(TEST_("basic"), test_basic); + g_test_add_func(TEST_("ind"), test_ind); + g_test_add_func(TEST_("req"), test_req); + g_test_add_func(TEST_("resp"), test_resp); + g_test_add_func(TEST_("ack"), test_ack); + g_test_add_func(TEST_("enabled"), test_enabled); + g_test_add_func(TEST_("death"), test_death); + test_init(&test_opt, argc, argv); + return g_test_run(); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/unit_registry/Makefile b/unit/unit_registry/Makefile new file mode 100644 index 0000000..e030b4b --- /dev/null +++ b/unit/unit_registry/Makefile @@ -0,0 +1,5 @@ +# -*- Mode: makefile-gmake -*- + +EXE = unit_registry + +include ../common/Makefile diff --git a/unit/unit_registry/unit_registry.c b/unit/unit_registry/unit_registry.c new file mode 100644 index 0000000..3dfaec2 --- /dev/null +++ b/unit/unit_registry/unit_registry.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_common.h" + +#include "radio_instance.h" +#include "radio_registry_p.h" + +#include + +static TestOpt test_opt; + +/*==========================================================================* + * null + *==========================================================================*/ + +static +void +test_null( + void) +{ + radio_registry_instance_added(NULL); + radio_registry_instance_removed(NULL); + radio_registry_remove_handler(NULL, 0); + radio_registry_remove_handlers(NULL, NULL, 0); + radio_registry_unref(NULL); + g_assert(!radio_registry_ref(NULL)); + g_assert(!radio_registry_add_instance_added_handler(NULL,NULL,NULL,NULL)); + g_assert(!radio_registry_add_instance_removed_handler(NULL,NULL,NULL,NULL)); +} + +/*==========================================================================* + * basic + *==========================================================================*/ + +static const char* test_basic_key = "foo"; +static const char* test_basic_bad_key = "bar"; + +static +void +test_basic_add_cb( + RadioRegistry* registry, + RadioInstance* radio, + gpointer user_data) +{ + (*((int*)user_data))++; +} + +static +void +test_basic_remove_cb( + RadioRegistry* registry, + const char* str, + gpointer user_data) +{ + g_assert_cmpstr(str, == ,test_basic_key); + (*((int*)user_data))++; +} + +static +void +test_basic( + void) +{ + RadioRegistry* reg = radio_registry_new(); + int add_count = 0, remove_count = 0; + gulong id[6]; + GObject* instance; + + g_assert(reg); + g_assert(reg == radio_registry_new()); /* New ref to the same instance */ + radio_registry_unref(reg); + + g_assert(!radio_registry_add_instance_added_handler(reg,NULL,NULL,NULL)); + g_assert(!radio_registry_add_instance_removed_handler(reg,NULL,NULL,NULL)); + + /* Add/remove handlers */ + id[0] = radio_registry_add_instance_added_handler(reg, "", + test_basic_add_cb, &add_count); + radio_registry_remove_handler(reg, id[0]); + id[0] = radio_registry_add_instance_added_handler(reg, NULL, + test_basic_add_cb, &add_count); + id[1] = radio_registry_add_instance_added_handler(reg, test_basic_bad_key, + test_basic_add_cb, &add_count); /* won't get called */ + + id[2] = radio_registry_add_instance_removed_handler(reg, NULL, + test_basic_remove_cb, &remove_count); + id[3] = radio_registry_add_instance_removed_handler(reg, "", + test_basic_remove_cb, &remove_count); + id[4] = radio_registry_add_instance_removed_handler(reg, test_basic_key, + test_basic_remove_cb, &remove_count); + id[5] = radio_registry_add_instance_removed_handler(reg, test_basic_bad_key, + test_basic_remove_cb, &remove_count); /* won't get called */ + + /* Well, this wouldn't be a real functional instance but we don't care */ + instance = g_object_new(RADIO_TYPE_INSTANCE, NULL); + radio_registry_instance_added(RADIO_INSTANCE(instance)); + g_assert_cmpint(add_count, == ,1); /* 1 out of 2 is called */ + g_assert_cmpint(remove_count, == ,0); + + radio_registry_instance_removed(test_basic_key); + g_assert_cmpint(add_count, == ,1); + g_assert_cmpint(remove_count, == ,3); /* 3 our of 4 are called */ + g_object_unref(instance); + + /* remove_all zeros the ids */ + radio_registry_remove_all_handlers(reg, id); + g_assert(!id[0]); + g_assert(!id[4]); + + radio_registry_remove_handler(reg, 0); /* No effect */ + radio_registry_unref(reg); +} + +/*==========================================================================* + * Common + *==========================================================================*/ + +#define TEST_PREFIX "/registry/" +#define TEST_(t) TEST_PREFIX t + +int main(int argc, char* argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func(TEST_("null"), test_null); + g_test_add_func(TEST_("basic"), test_basic); + test_init(&test_opt, argc, argv); + return g_test_run(); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/unit_util/Makefile b/unit/unit_util/Makefile new file mode 100644 index 0000000..4a4f095 --- /dev/null +++ b/unit/unit_util/Makefile @@ -0,0 +1,5 @@ +# -*- Mode: makefile-gmake -*- + +EXE = unit_util + +include ../common/Makefile diff --git a/unit/unit_util/unit_util.c b/unit/unit_util/unit_util.c new file mode 100644 index 0000000..10f0a0e --- /dev/null +++ b/unit/unit_util/unit_util.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2021 Jolla Ltd. + * Copyright (C) 2021 Slava Monich + * + * You may use this file under the terms of the BSD license as follows: + * + * 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, this list of conditions and the following disclaimer. + * 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. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * 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 ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * any official policies, either expressed or implied. + */ + +#include "test_common.h" + +#include "radio_util.h" + +#define UNKNOWN_VALUE (0x7fffffff) +#define UNKNOWN_REQ ((RADIO_REQ)UNKNOWN_VALUE) +#define UNKNOWN_IND ((RADIO_REQ)UNKNOWN_VALUE) +#define UNKNOWN_RESP ((RADIO_RESP)UNKNOWN_VALUE) + +static TestOpt test_opt; + +/*==========================================================================* + * req_name + *==========================================================================*/ + +static +void +test_req_name( + void) +{ + g_assert(!radio_req_name(UNKNOWN_REQ)); + g_assert(!radio_req_name(RADIO_REQ_ANY)); + g_assert_cmpstr(radio_req_name(RADIO_REQ_GET_ICC_CARD_STATUS),==, + "getIccCardStatus"); + g_assert_cmpstr(radio_req_name(RADIO_REQ_START_NETWORK_SCAN),==, + "startNetworkScan"); + g_assert_cmpstr(radio_req_name(RADIO_REQ_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA),==, + "setSignalStrengthReportingCriteria"); + g_assert_cmpstr(radio_req_name(RADIO_REQ_SET_SYSTEM_SELECTION_CHANNELS),==, + "setSystemSelectionChannels"); + g_assert_cmpstr(radio_req_name(RADIO_REQ_EMERGENCY_DIAL),==, + "emergencyDial"); +} + +/*==========================================================================* + * resp_name + *==========================================================================*/ + +static +void +test_resp_name( + void) +{ + g_assert(!radio_resp_name(UNKNOWN_RESP)); + g_assert(!radio_resp_name(RADIO_RESP_ANY)); + g_assert_cmpstr(radio_resp_name(RADIO_RESP_GET_ICC_CARD_STATUS),==, + "getIccCardStatusResponse"); + g_assert_cmpstr(radio_resp_name(RADIO_RESP_START_NETWORK_SCAN),==, + "startNetworkScanResponse"); + g_assert_cmpstr(radio_resp_name(RADIO_RESP_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA),==, + "setSignalStrengthReportingCriteriaResponse"); + g_assert_cmpstr(radio_resp_name(RADIO_RESP_SET_SYSTEM_SELECTION_CHANNELS),==, + "setSystemSelectionChannelsResponse"); + g_assert_cmpstr(radio_resp_name(RADIO_RESP_EMERGENCY_DIAL),==, + "emergencyDialResponse"); +} + +/*==========================================================================* + * ind_name + *==========================================================================*/ + +static +void +test_ind_name( + void) +{ + g_assert(!radio_ind_name(UNKNOWN_IND)); + g_assert(!radio_ind_name(RADIO_IND_ANY)); + g_assert_cmpstr(radio_ind_name(RADIO_IND_RADIO_STATE_CHANGED),==, + "radioStateChanged"); + g_assert_cmpstr(radio_ind_name(RADIO_IND_NETWORK_SCAN_RESULT),==, + "networkScanResult"); + g_assert_cmpstr(radio_ind_name(RADIO_IND_CURRENT_LINK_CAPACITY_ESTIMATE),==, + "currentLinkCapacityEstimate"); + g_assert_cmpstr(radio_ind_name(RADIO_IND_CURRENT_EMERGENCY_NUMBER_LIST),==, + "currentEmergencyNumberList"); +} + +/*==========================================================================* + * req_resp + *==========================================================================*/ + +static +void +test_req_resp( + void) +{ + g_assert_cmpint(radio_req_resp(UNKNOWN_REQ), == ,RADIO_RESP_NONE); + g_assert_cmpint(radio_req_resp(RADIO_REQ_ANY), == ,RADIO_RESP_NONE); + g_assert_cmpint(radio_req_resp(RADIO_REQ_GET_ICC_CARD_STATUS),==, + RADIO_RESP_GET_ICC_CARD_STATUS); + g_assert_cmpint(radio_req_resp(RADIO_REQ_START_NETWORK_SCAN),==, + RADIO_RESP_START_NETWORK_SCAN); + g_assert_cmpint(radio_req_resp(RADIO_REQ_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA),==, + RADIO_RESP_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA); + g_assert_cmpint(radio_req_resp(RADIO_REQ_SET_SYSTEM_SELECTION_CHANNELS),==, + RADIO_RESP_SET_SYSTEM_SELECTION_CHANNELS); + g_assert_cmpint(radio_req_resp(RADIO_REQ_EMERGENCY_DIAL),==, + RADIO_RESP_EMERGENCY_DIAL); +} + +/*==========================================================================* + * Common + *==========================================================================*/ + +#define TEST_PREFIX "/util/" +#define TEST_(t) TEST_PREFIX t + +int main(int argc, char* argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func(TEST_("req_name"), test_req_name); + g_test_add_func(TEST_("resp_name"), test_resp_name); + g_test_add_func(TEST_("ind_name"), test_ind_name); + g_test_add_func(TEST_("req_resp"), test_req_resp); + test_init(&test_opt, argc, argv); + return g_test_run(); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */