mirror of
https://github.com/wiiu-env/libnotifications.git
synced 2026-03-21 18:04:09 -05:00
Properly implement NotificationModule_SetDefaultValue typechecks for C and C++
This commit is contained in:
parent
8caeeaa8a8
commit
3792c97d68
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Enforce Unix line endings for shell scripts
|
||||
*.sh text eol=lf
|
||||
2
.github/workflows/pr.yml
vendored
2
.github/workflows/pr.yml
vendored
|
|
@ -20,6 +20,8 @@ jobs:
|
|||
docker build . -t tmp
|
||||
docker build . -f Dockerfile.buildlocal -t builder
|
||||
docker run --rm -v ${PWD}:/project builder make
|
||||
docker run --rm -v ${PWD}:/project builder /bin/bash -c "make install && chmod +x tests/parameter_checker/runner.sh && cd tests/parameter_checker && ./runner.sh"
|
||||
docker run --rm -v ${PWD}:/project builder /bin/bash -c "make install && chmod +x tests/parameter_checker/negative_runner.sh && cd tests/parameter_checker && ./negative_runner.sh"
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: lib
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -7,3 +7,7 @@ CMakeLists.txt
|
|||
.idea/
|
||||
cmake-build-debug/
|
||||
share/libfunctionpatcher.ld
|
||||
*.elf
|
||||
*.rpx
|
||||
*.wuhb
|
||||
tests/parameter_checker/build/
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM ghcr.io/wiiu-env/devkitppc:20240423
|
||||
FROM ghcr.io/wiiu-env/devkitppc:20250608
|
||||
|
||||
WORKDIR tmp_build
|
||||
COPY . .
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
FROM ghcr.io/wiiu-env/devkitppc:20240423
|
||||
FROM ghcr.io/wiiu-env/devkitppc:20250608
|
||||
|
||||
WORKDIR project
|
||||
|
|
@ -374,23 +374,20 @@ NotificationModuleStatus NotificationModule_FinishDynamicNotificationWithShake(N
|
|||
float durationBeforeFadeOutInSeconds,
|
||||
float shakeDuration);
|
||||
|
||||
// Copy pasted from libcurl...
|
||||
/* the typechecker doesn't work in C++ (yet) */
|
||||
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
|
||||
((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \
|
||||
!defined(__cplusplus)
|
||||
#include "typecheck-gcc.h"
|
||||
#else
|
||||
#if defined(__STDC__) && (__STDC__ >= 1)
|
||||
/* This preprocessor magic that replaces a call with the exact same call is
|
||||
only done to make sure application authors pass exactly three arguments
|
||||
to these functions. */
|
||||
|
||||
#define NotificationModule_SetDefaultValue(type, valueType, param) NotificationModule_SetDefaultValue(type, valueType, param)
|
||||
#endif /* __STDC__ >= 1 */
|
||||
#endif /* gcc >= 4.3 && !__cplusplus */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// Idea taken from libcurl
|
||||
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
|
||||
((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
|
||||
#include "typechecks-gcc.h"
|
||||
#else
|
||||
#if defined(__STDC__) && (__STDC__ >= 1)
|
||||
/* This preprocessor magic that replaces a call with the exact same call is
|
||||
only done to make sure application authors pass exactly three arguments
|
||||
to these functions. */
|
||||
|
||||
#define NotificationModule_SetDefaultValue(type, valueType, param) NotificationModule_SetDefaultValue(type, valueType, param)
|
||||
#endif /* __STDC__ >= 1 */
|
||||
#endif /* gcc >= 4.3 */
|
||||
|
|
|
|||
172
include/notifications/typechecks-gcc.h
Normal file
172
include/notifications/typechecks-gcc.h
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
#ifndef NOTIFICATIONS_TYPECHECK_GCC_H
|
||||
#define NOTIFICATIONS_TYPECHECK_GCC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstddef> /* For std::nullptr_t */
|
||||
#else
|
||||
#include <stddef.h> /* For NULL */
|
||||
#endif
|
||||
|
||||
/* * Warning generators (Common to C and C++)
|
||||
* These define static functions that trigger a compiler warning when called.
|
||||
*/
|
||||
#define _NM_WARNING(id, message) \
|
||||
static void __attribute__((__warning__(message))) \
|
||||
__attribute__((__unused__)) __attribute__((__noinline__)) \
|
||||
id(void) { __asm__(""); }
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
_NM_WARNING(_nm_warn_NMColor, "NotificationModule_SetDefaultValue expects 'NMColor' for this option.")
|
||||
_NM_WARNING(_nm_warn_float, "NotificationModule_SetDefaultValue expects 'float' or 'double' for this option.")
|
||||
_NM_WARNING(_nm_warn_callback, "NotificationModule_SetDefaultValue expects 'NotificationModuleNotificationFinishedCallback' for this option.")
|
||||
_NM_WARNING(_nm_warn_context, "NotificationModule_SetDefaultValue expects 'void*' for this option.")
|
||||
_NM_WARNING(_nm_warn_bool, "NotificationModule_SetDefaultValue expects 'bool' (or 'int') for this option.")
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* =========================================================================
|
||||
* IMPLEMENTATION SELECTION
|
||||
* ========================================================================= */
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
/* ==========================================
|
||||
* C++17 Implementation
|
||||
* Only active if -std=c++17 or higher is used.
|
||||
* ========================================== */
|
||||
#if __cplusplus >= 201703L
|
||||
|
||||
namespace NM_Check {
|
||||
/* NMColor Checker */
|
||||
inline bool check_NMColor(NMColor) { return true; }
|
||||
inline bool check_NMColor(void *) { return false; } /* Sink for NULL */
|
||||
template<typename T>
|
||||
inline bool check_NMColor(T) { return false; }
|
||||
|
||||
/* Float Checker (accepts float/double) */
|
||||
inline bool check_float(float) { return true; }
|
||||
inline bool check_float(double) { return true; }
|
||||
inline bool check_float(void *) { return false; } /* Sink for NULL */
|
||||
template<typename T>
|
||||
inline bool check_float(T) { return false; }
|
||||
|
||||
/* Bool Checker (accepts bool/int) */
|
||||
inline bool check_bool(bool) { return true; }
|
||||
inline bool check_bool(int) { return true; }
|
||||
inline bool check_bool(void *) { return false; } /* Sink for NULL */
|
||||
template<typename T>
|
||||
inline bool check_bool(T) { return false; }
|
||||
|
||||
/* Callback Checker */
|
||||
inline bool check_callback(NotificationModuleNotificationFinishedCallback) { return true; }
|
||||
/* Explicit void* (e.g. casting) */
|
||||
inline bool check_callback(void *) { return true; }
|
||||
inline bool check_callback(int i) { return i == 0; }
|
||||
inline bool check_callback(long i) { return i == 0; }
|
||||
inline bool check_callback(std::nullptr_t) { return true; }
|
||||
template<typename T>
|
||||
inline bool check_callback(T) { return false; }
|
||||
|
||||
/* Context Checker */
|
||||
inline bool check_context(void *) { return true; }
|
||||
inline bool check_context(int i) { return i == 0; }
|
||||
inline bool check_context(long i) { return i == 0; }
|
||||
inline bool check_context(std::nullptr_t) { return true; }
|
||||
template<typename T>
|
||||
inline bool check_context(T) { return false; }
|
||||
} // namespace NM_Check
|
||||
|
||||
/* Macros mapping to C++ namespace calls */
|
||||
#define _nm_is_NMColor(x) NM_Check::check_NMColor(x)
|
||||
#define _nm_is_float(x) NM_Check::check_float(x)
|
||||
#define _nm_is_bool(x) NM_Check::check_bool(x)
|
||||
#define _nm_is_callback(x) NM_Check::check_callback(x)
|
||||
#define _nm_is_context(x) NM_Check::check_context(x)
|
||||
|
||||
#else
|
||||
/* ==========================================
|
||||
* Pre-C++17 Implementation
|
||||
* Checks are DISABLED to avoid errors in older versions.
|
||||
* ========================================== */
|
||||
#define _nm_is_NMColor(x) (1)
|
||||
#define _nm_is_float(x) (1)
|
||||
#define _nm_is_bool(x) (1)
|
||||
#define _nm_is_callback(x) (1)
|
||||
#define _nm_is_context(x) (1)
|
||||
#endif
|
||||
|
||||
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
|
||||
/* ==========================================
|
||||
* C11 Implementation (using _Generic)
|
||||
* ========================================== */
|
||||
|
||||
#define _nm_is_NMColor(x) _Generic((x), \
|
||||
NMColor : 1, \
|
||||
default : 0)
|
||||
|
||||
#define _nm_is_float(x) _Generic((x), \
|
||||
float : 1, \
|
||||
double : 1, \
|
||||
default : 0)
|
||||
|
||||
#define _nm_is_bool(x) _Generic((x), \
|
||||
_Bool : 1, \
|
||||
int : 1, \
|
||||
default : 0)
|
||||
|
||||
#define _nm_is_callback(x) _Generic((x), \
|
||||
NotificationModuleNotificationFinishedCallback : 1, \
|
||||
void * : 1, \
|
||||
default : 0)
|
||||
|
||||
#define _nm_is_context(x) _Generic((x), \
|
||||
void * : 1, \
|
||||
default : 0)
|
||||
|
||||
#else
|
||||
|
||||
/* ==========================================
|
||||
* Legacy C Implementation
|
||||
* Partial checking only.
|
||||
* ========================================== */
|
||||
|
||||
#define _nm_is_type(x, type) __builtin_types_compatible_p(__typeof__(x), type)
|
||||
|
||||
/* Disable complex checks in old C */
|
||||
#define _nm_is_NMColor(x) (1)
|
||||
#define _nm_is_callback(x) (1)
|
||||
#define _nm_is_context(x) (1)
|
||||
|
||||
/* Scalars are safe to check */
|
||||
#define _nm_is_float(x) (_nm_is_type(x, float) || _nm_is_type(x, double))
|
||||
#define _nm_is_bool(x) (_nm_is_type(x, int) || _nm_is_type(x, _Bool))
|
||||
|
||||
#endif
|
||||
|
||||
#define NotificationModule_SetDefaultValue(type, option, value) \
|
||||
__extension__({ \
|
||||
if (__builtin_constant_p(option)) { \
|
||||
if ((option == NOTIFICATION_MODULE_DEFAULT_OPTION_BACKGROUND_COLOR) && !_nm_is_NMColor(value)) \
|
||||
_nm_warn_NMColor(); \
|
||||
else if ((option == NOTIFICATION_MODULE_DEFAULT_OPTION_TEXT_COLOR) && !_nm_is_NMColor(value)) \
|
||||
_nm_warn_NMColor(); \
|
||||
else if ((option == NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT) && !_nm_is_float(value)) \
|
||||
_nm_warn_float(); \
|
||||
else if ((option == NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION) && !_nm_is_callback(value)) \
|
||||
_nm_warn_callback(); \
|
||||
else if ((option == NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION_CONTEXT) && !_nm_is_context(value)) \
|
||||
_nm_warn_context(); \
|
||||
else if ((option == NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN) && !_nm_is_bool(value)) \
|
||||
_nm_warn_bool(); \
|
||||
} \
|
||||
(NotificationModule_SetDefaultValue)(type, option, value); \
|
||||
})
|
||||
|
||||
#endif /* NOTIFICATIONS_TYPECHECK_GCC_H */
|
||||
184
tests/parameter_checker/Makefile
Normal file
184
tests/parameter_checker/Makefile
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
#-------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# APP_NAME sets the long name of the application
|
||||
# APP_SHORTNAME sets the short name of the application
|
||||
# APP_AUTHOR sets the author of the application
|
||||
#-------------------------------------------------------------------------------
|
||||
#APP_NAME := Application Name
|
||||
#APP_SHORTNAME := App Name
|
||||
#APP_AUTHOR := Built with devkitPPC & wut
|
||||
|
||||
include $(DEVKITPRO)/wut/share/wut_rules
|
||||
|
||||
WUMS_ROOT := $(DEVKITPRO)/wums
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# CONTENT is the path to the bundled folder that will be mounted as /vol/content/
|
||||
# ICON is the game icon, leave blank to use default rule
|
||||
# TV_SPLASH is the image displayed during bootup on the TV, leave blank to use default rule
|
||||
# DRC_SPLASH is the image displayed during bootup on the DRC, leave blank to use default rule
|
||||
#-------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
# Default to src_cpp if not specified
|
||||
TEST_SRC ?= src_cpp
|
||||
SOURCES := $(TEST_SRC)
|
||||
DATA := data
|
||||
INCLUDES := $(TEST_SRC)
|
||||
CONTENT :=
|
||||
ICON :=
|
||||
TV_SPLASH :=
|
||||
DRC_SPLASH :=
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#-------------------------------------------------------------------------------
|
||||
CFLAGS := -g -Wall -Werror -O2 -ffunction-sections \
|
||||
$(MACHDEP) $(USER_CFLAGS)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) $(USER_CXXFLAGS)
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lwut -lnotifications -lstdc++
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
# containing include and lib
|
||||
#-------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#-------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#-------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#-------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#-------------------------------------------------------------------------------
|
||||
else
|
||||
#-------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#-------------------------------------------------------------------------------
|
||||
endif
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
ifneq (,$(strip $(CONTENT)))
|
||||
export APP_CONTENT := $(TOPDIR)/$(CONTENT)
|
||||
endif
|
||||
|
||||
ifneq (,$(strip $(ICON)))
|
||||
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||
else ifneq (,$(wildcard $(TOPDIR)/$(TARGET).png))
|
||||
export APP_ICON := $(TOPDIR)/$(TARGET).png
|
||||
else ifneq (,$(wildcard $(TOPDIR)/icon.png))
|
||||
export APP_ICON := $(TOPDIR)/icon.png
|
||||
endif
|
||||
|
||||
ifneq (,$(strip $(TV_SPLASH)))
|
||||
export APP_TV_SPLASH := $(TOPDIR)/$(TV_SPLASH)
|
||||
else ifneq (,$(wildcard $(TOPDIR)/tv-splash.png))
|
||||
export APP_TV_SPLASH := $(TOPDIR)/tv-splash.png
|
||||
else ifneq (,$(wildcard $(TOPDIR)/splash.png))
|
||||
export APP_TV_SPLASH := $(TOPDIR)/splash.png
|
||||
endif
|
||||
|
||||
ifneq (,$(strip $(DRC_SPLASH)))
|
||||
export APP_DRC_SPLASH := $(TOPDIR)/$(DRC_SPLASH)
|
||||
else ifneq (,$(wildcard $(TOPDIR)/drc-splash.png))
|
||||
export APP_DRC_SPLASH := $(TOPDIR)/drc-splash.png
|
||||
else ifneq (,$(wildcard $(TOPDIR)/splash.png))
|
||||
export APP_DRC_SPLASH := $(TOPDIR)/splash.png
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).wuhb $(TARGET).rpx $(TARGET).elf
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#-------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).wuhb
|
||||
|
||||
$(OUTPUT).wuhb : $(OUTPUT).rpx
|
||||
$(OUTPUT).rpx : $(OUTPUT).elf
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#-------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#-------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
endif
|
||||
#-------------------------------------------------------------------------------
|
||||
119
tests/parameter_checker/negative_runner.sh
Normal file
119
tests/parameter_checker/negative_runner.sh
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Define Colors
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo "========================================"
|
||||
echo "Starting Advanced Compilation Matrix"
|
||||
echo "========================================"
|
||||
|
||||
# Track overall status
|
||||
OVERALL_SUCCESS=true
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Helper Function: Run a specific Negative Test
|
||||
# ---------------------------------------------------------
|
||||
run_check() {
|
||||
TEST_NAME=$1 # e.g., TEST_FAIL_COLOR
|
||||
SRC_DIR=$2 # e.g., src_fail_cpp
|
||||
STD=$3 # e.g., gnu++17
|
||||
TYPE=$4 # CXX or C
|
||||
|
||||
echo -n "Checking $TEST_NAME [$STD]... "
|
||||
|
||||
# 1. POSITIVE VERIFICATION (Must Compile with -DMAKE_VALID)
|
||||
make clean --no-print-directory > /dev/null 2>&1
|
||||
|
||||
# Construct Flags
|
||||
if [ "$TYPE" == "CXX" ]; then
|
||||
FLAGS="-std=$STD -D$TEST_NAME -DMAKE_VALID"
|
||||
OUT=$(make TEST_SRC=$SRC_DIR USER_CXXFLAGS="$FLAGS" --no-print-directory 2>&1)
|
||||
else
|
||||
FLAGS="-std=$STD -D$TEST_NAME -DMAKE_VALID"
|
||||
OUT=$(make TEST_SRC=$SRC_DIR USER_CFLAGS="$FLAGS" --no-print-directory 2>&1)
|
||||
fi
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}[ERROR]${NC}"
|
||||
echo " -> Valid code failed to build! Setup is broken."
|
||||
echo "$OUT"
|
||||
OVERALL_SUCCESS=false
|
||||
return
|
||||
fi
|
||||
|
||||
# 2. NEGATIVE VERIFICATION (Must FAIL without -DMAKE_VALID)
|
||||
make clean --no-print-directory > /dev/null 2>&1
|
||||
|
||||
if [ "$TYPE" == "CXX" ]; then
|
||||
FLAGS="-std=$STD -D$TEST_NAME"
|
||||
OUT=$(make TEST_SRC=$SRC_DIR USER_CXXFLAGS="$FLAGS" --no-print-directory 2>&1)
|
||||
else
|
||||
FLAGS="-std=$STD -D$TEST_NAME"
|
||||
OUT=$(make TEST_SRC=$SRC_DIR USER_CFLAGS="$FLAGS" --no-print-directory 2>&1)
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${RED}[ERROR]${NC}"
|
||||
echo " -> Invalid code SUCCEEDED! Type check failed to catch error."
|
||||
echo "make TEST_SRC=$SRC_DIR USER_CFLAGS="$FLAGS" --no-print-directory 2>&1"
|
||||
OVERALL_SUCCESS=false
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}[OK]${NC}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# C++ NEGATIVE TESTS
|
||||
# ---------------------------------------------------------
|
||||
# Note: C++ Type checks are only active on C++17 and newer.
|
||||
CPP_FULL_CHECK_VERSIONS=("gnu++17" "gnu++20" "gnu++23")
|
||||
|
||||
for std in "${CPP_FULL_CHECK_VERSIONS[@]}"; do
|
||||
run_check "TEST_FAIL_COLOR" "src_fail_cpp" "$std" "CXX"
|
||||
run_check "TEST_FAIL_DURATION" "src_fail_cpp" "$std" "CXX"
|
||||
run_check "TEST_FAIL_CALLBACK" "src_fail_cpp" "$std" "CXX"
|
||||
run_check "TEST_FAIL_CONTEXT" "src_fail_cpp" "$std" "CXX"
|
||||
run_check "TEST_FAIL_BOOL" "src_fail_cpp" "$std" "CXX"
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# C NEGATIVE TESTS
|
||||
# ---------------------------------------------------------
|
||||
# C11 and newer have support for _Generic (Full Checks)
|
||||
C_FULL_CHECK_VERSIONS=("gnu11" "gnu17" "gnu18" "gnu2x")
|
||||
# C99 only supports built-in compatibility checks (Scalar only: float, bool)
|
||||
C_SCALAR_CHECK_VERSIONS=("gnu99")
|
||||
|
||||
# 1. Full Checks (Color, Callback, Context) - C11+ Only
|
||||
for std in "${C_FULL_CHECK_VERSIONS[@]}"; do
|
||||
run_check "TEST_FAIL_COLOR" "src_fail_c" "$std" "C"
|
||||
run_check "TEST_FAIL_CALLBACK" "src_fail_c" "$std" "C"
|
||||
run_check "TEST_FAIL_CONTEXT" "src_fail_c" "$std" "C"
|
||||
run_check "TEST_FAIL_DURATION" "src_fail_c" "$std" "C"
|
||||
run_check "TEST_FAIL_BOOL" "src_fail_c" "$std" "C"
|
||||
done
|
||||
|
||||
# 2. Scalar Checks (Float, Bool) - C99+
|
||||
for std in "${C_SCALAR_CHECK_VERSIONS[@]}"; do
|
||||
run_check "TEST_FAIL_COLOR" "src_fail_c" "$std" "C"
|
||||
run_check "TEST_FAIL_DURATION" "src_fail_c" "$std" "C"
|
||||
run_check "TEST_FAIL_BOOL" "src_fail_c" "$std" "C"
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# FINAL STATUS
|
||||
# ---------------------------------------------------------
|
||||
if [ "$OVERALL_SUCCESS" = true ]; then
|
||||
echo "========================================"
|
||||
echo -e "${GREEN}All checks passed successfully!${NC}"
|
||||
echo "========================================"
|
||||
exit 0
|
||||
else
|
||||
echo "========================================"
|
||||
echo -e "${RED}Some checks failed.${NC}"
|
||||
echo "========================================"
|
||||
exit 1
|
||||
fi
|
||||
77
tests/parameter_checker/runner.sh
Normal file
77
tests/parameter_checker/runner.sh
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
|
||||
echo "========================================"
|
||||
echo "Starting Compilation Matrix"
|
||||
echo "========================================"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 1. POSITIVE TESTS (Should Compile)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# C++ Standards
|
||||
CPP_STANDARDS=("gnu++11" "gnu++14" "gnu++17" "gnu++2a")
|
||||
|
||||
for std in "${CPP_STANDARDS[@]}"; do
|
||||
echo "[PASS-CHECK] C++ Source with -std=$std"
|
||||
make clean --no-print-directory
|
||||
make TEST_SRC=src_cpp USER_CXXFLAGS="-std=$std" --no-print-directory
|
||||
echo " -> SUCCESS"
|
||||
done
|
||||
|
||||
# C Standards
|
||||
C_STANDARDS=("gnu99" "gnu11" "gnu17")
|
||||
|
||||
for std in "${C_STANDARDS[@]}"; do
|
||||
echo "[PASS-CHECK] C Source with -std=$std"
|
||||
make clean --no-print-directory
|
||||
make TEST_SRC=src_c USER_CFLAGS="-std=$std" --no-print-directory
|
||||
echo " -> SUCCESS"
|
||||
done
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 2. NEGATIVE TESTS (Should FAIL to Compile)
|
||||
# ------------------------------------------------------------------
|
||||
echo "========================================"
|
||||
echo "Starting Negative Tests (Expect Errors)"
|
||||
echo "========================================"
|
||||
|
||||
FAIL_CASES=(
|
||||
"src_fail/fail_color"
|
||||
"src_fail/fail_duration"
|
||||
"src_fail/fail_callback"
|
||||
"src_fail/fail_bool"
|
||||
)
|
||||
|
||||
# Temporarily turn off 'set -e' so we can capture the failure
|
||||
set +e
|
||||
|
||||
for test_dir in "${FAIL_CASES[@]}"; do
|
||||
echo "[FAIL-CHECK] Testing $test_dir (Should fail build)"
|
||||
|
||||
make clean --no-print-directory > /dev/null 2>&1
|
||||
|
||||
# Run Make and capture output to prevent cluttering logs,
|
||||
# unless you want to see the specific error.
|
||||
OUTPUT=$(make TEST_SRC=$test_dir --no-print-directory 2>&1)
|
||||
EXIT_CODE=$?
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo " -> ERROR: Build succeeded but SHOULD HAVE FAILED!"
|
||||
echo " (Type checks failed to catch the invalid parameter)"
|
||||
exit 1
|
||||
else
|
||||
echo " -> SUCCESS: Build failed as expected."
|
||||
# Optional: Check if the output contains the specific warning message?
|
||||
# echo "$OUTPUT" | grep "NotificationModule_SetDefaultValue expects"
|
||||
fi
|
||||
done
|
||||
|
||||
# Re-enable exit on error
|
||||
set -e
|
||||
|
||||
echo "========================================"
|
||||
echo "All tests passed successfully!"
|
||||
echo "========================================"
|
||||
54
tests/parameter_checker/src_c/main.c
Normal file
54
tests/parameter_checker/src_c/main.c
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#include <coreinit/thread.h>
|
||||
#include <notifications/notifications.h>
|
||||
|
||||
// Dummy callback for C
|
||||
void my_c_callback(NotificationModuleHandle h, void* ctx) {
|
||||
(void)h;
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
NotificationModule_InitLibrary();
|
||||
|
||||
// Setup variables
|
||||
NMColor color = {255, 0, 0, 255};
|
||||
float duration = 3.0f;
|
||||
int ctx_data = 100;
|
||||
|
||||
// Test 1: Background Color
|
||||
// C11+ uses _Generic to check types here.
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_BACKGROUND_COLOR,
|
||||
color
|
||||
);
|
||||
|
||||
// Test 2: Duration
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT,
|
||||
duration
|
||||
);
|
||||
|
||||
// Test 3: Callback
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION,
|
||||
my_c_callback
|
||||
);
|
||||
|
||||
// Test 4: Context
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION_CONTEXT,
|
||||
(void*) &ctx_data
|
||||
);
|
||||
|
||||
NotificationModule_AddInfoNotification("C Compatibility Test");
|
||||
|
||||
NotificationModule_DeInitLibrary();
|
||||
return 0;
|
||||
}
|
||||
68
tests/parameter_checker/src_cpp/main.cpp
Normal file
68
tests/parameter_checker/src_cpp/main.cpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#include <coreinit/thread.h>
|
||||
#include <notifications/notifications.h>
|
||||
|
||||
// Dummy callback for testing
|
||||
void my_callback(NotificationModuleHandle h, void* ctx) {
|
||||
(void)h;
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
// 1. Initialize the library
|
||||
NotificationModule_InitLibrary();
|
||||
|
||||
// 2. Test Type Checks (Valid Cases)
|
||||
// These should compile successfully. If the type checks are broken,
|
||||
// -Werror will cause the build to fail here.
|
||||
|
||||
NMColor color = {255, 255, 255, 255};
|
||||
float duration = 5.0f;
|
||||
bool keep = true;
|
||||
int ctx_data = 1;
|
||||
|
||||
// Background Color
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_BACKGROUND_COLOR,
|
||||
color
|
||||
);
|
||||
|
||||
// Duration
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT,
|
||||
duration
|
||||
);
|
||||
|
||||
// Callback
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION,
|
||||
my_callback
|
||||
);
|
||||
|
||||
// Context
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION_CONTEXT,
|
||||
(void*)&ctx_data
|
||||
);
|
||||
|
||||
// Keep
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN,
|
||||
keep
|
||||
);
|
||||
|
||||
// 3. Test API usage
|
||||
NotificationModule_AddInfoNotification("CI Test: Build Successful!");
|
||||
|
||||
// Deinit
|
||||
NotificationModule_DeInitLibrary();
|
||||
|
||||
return 0;
|
||||
}
|
||||
80
tests/parameter_checker/src_fail_c/main.c
Normal file
80
tests/parameter_checker/src_fail_c/main.c
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#include <notifications/notifications.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Helper macros
|
||||
#ifdef MAKE_VALID
|
||||
#define ARG(valid, invalid) (valid)
|
||||
#else
|
||||
#define ARG(valid, invalid) (invalid)
|
||||
#endif
|
||||
|
||||
void my_cb(NotificationModuleHandle h, void* c) { (void)h; (void)c; }
|
||||
void my_broken_cb(NotificationModuleHandle h, void* c, int) { (void)h; (void)c; }
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
NotificationModule_InitLibrary();
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TEST CASE: FAIL_COLOR
|
||||
// ---------------------------------------------------------
|
||||
#ifdef TEST_FAIL_COLOR
|
||||
NMColor valid_col = {255, 0, 0, 255};
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_BACKGROUND_COLOR,
|
||||
ARG(valid_col, 0xFFFFFFFF)
|
||||
);
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TEST CASE: FAIL_DURATION
|
||||
// ---------------------------------------------------------
|
||||
#ifdef TEST_FAIL_DURATION
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT,
|
||||
ARG(5.0f, 5)
|
||||
);
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TEST CASE: FAIL_CALLBACK
|
||||
// ---------------------------------------------------------
|
||||
#ifdef TEST_FAIL_CALLBACK
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION,
|
||||
ARG(my_cb, my_broken_cb)
|
||||
);
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TEST CASE: FAIL_CONTEXT
|
||||
// ---------------------------------------------------------
|
||||
#ifdef TEST_FAIL_CONTEXT
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION_CONTEXT,
|
||||
ARG(NULL, 1.5f)
|
||||
);
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TEST CASE: FAIL_BOOL
|
||||
// ---------------------------------------------------------
|
||||
#ifdef TEST_FAIL_BOOL
|
||||
#ifdef MAKE_VALID
|
||||
#else
|
||||
int dummy_int = 0;
|
||||
#endif
|
||||
// For C, 1 is a valid boolean (int), so we use a pointer as invalid
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN,
|
||||
ARG(1, &dummy_int)
|
||||
);
|
||||
#endif
|
||||
|
||||
NotificationModule_DeInitLibrary();
|
||||
return 0;
|
||||
}
|
||||
80
tests/parameter_checker/src_fail_cpp/main.cpp
Normal file
80
tests/parameter_checker/src_fail_cpp/main.cpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#include <notifications/notifications.h>
|
||||
|
||||
// Helper macros to switch between Valid and Invalid data
|
||||
#ifdef MAKE_VALID
|
||||
#define ARG(valid, invalid) (valid)
|
||||
#else
|
||||
#define ARG(valid, invalid) (invalid)
|
||||
#endif
|
||||
|
||||
// Dummy callback
|
||||
void my_cb(NotificationModuleHandle h, void* c) { (void)h; (void)c; }
|
||||
void my_broken_cb(NotificationModuleHandle h, void* c, int) { (void)h; (void)c; }
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
NotificationModule_InitLibrary();
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TEST CASE: FAIL_COLOR (Background Color)
|
||||
// ---------------------------------------------------------
|
||||
#ifdef TEST_FAIL_COLOR
|
||||
NMColor valid_col = {255, 0, 0, 255};
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_BACKGROUND_COLOR,
|
||||
ARG(valid_col, 0xFFFFFFFF) // Invalid: int
|
||||
);
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TEST CASE: FAIL_DURATION (Float)
|
||||
// ---------------------------------------------------------
|
||||
#ifdef TEST_FAIL_DURATION
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT,
|
||||
ARG(5.0f, 5) // Invalid: int literal
|
||||
);
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TEST CASE: FAIL_CALLBACK (Function Pointer)
|
||||
// ---------------------------------------------------------
|
||||
#ifdef TEST_FAIL_CALLBACK
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION,
|
||||
ARG(my_cb, my_broken_cb) // Invalid: nullptr
|
||||
);
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TEST CASE: FAIL_CONTEXT (Void Pointer)
|
||||
// ---------------------------------------------------------
|
||||
#ifdef TEST_FAIL_CONTEXT
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION_CONTEXT,
|
||||
ARG(nullptr, 1.5f) // Invalid: float (nullptr is valid for void*)
|
||||
);
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TEST CASE: FAIL_BOOL (Boolean)
|
||||
// ---------------------------------------------------------
|
||||
#ifdef TEST_FAIL_BOOL
|
||||
#ifdef MAKE_VALID
|
||||
bool valid_bool = true;
|
||||
#else
|
||||
int dummy_int = 0;
|
||||
#endif
|
||||
NotificationModule_SetDefaultValue(
|
||||
NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
|
||||
NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN,
|
||||
ARG(valid_bool, &dummy_int) // Invalid: pointer
|
||||
);
|
||||
#endif
|
||||
|
||||
NotificationModule_DeInitLibrary();
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user