mirror of
https://github.com/wiiu-env/WiiUPluginSystem.git
synced 2024-11-15 23:35:15 +01:00
Compare commits
No commits in common. "3c26ecd473a2ce604e4b01bbda888a18d3b9bf8d" and "f0bf943f723582e354f71345c6f4c9aeda593ea0" have entirely different histories.
3c26ecd473
...
f0bf943f72
6
.github/workflows/pr.yml
vendored
6
.github/workflows/pr.yml
vendored
@ -30,8 +30,6 @@ jobs:
|
|||||||
- name: clang-format
|
- name: clang-format
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin/src
|
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin/src
|
||||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin_cpp/src
|
|
||||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/storage_test_plugin/src --exclude ./plugins/storage_test_plugin/src/catch2
|
|
||||||
build-examples:
|
build-examples:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: clang-format-examples
|
needs: clang-format-examples
|
||||||
@ -42,10 +40,6 @@ jobs:
|
|||||||
docker build . -f Dockerfile.buildexamples -t builder
|
docker build . -f Dockerfile.buildexamples -t builder
|
||||||
cd ./plugins/example_plugin
|
cd ./plugins/example_plugin
|
||||||
docker run --rm -v ${PWD}:/project builder make
|
docker run --rm -v ${PWD}:/project builder make
|
||||||
cd ../example_plugin_cpp
|
|
||||||
docker run --rm -v ${PWD}:/project builder make
|
|
||||||
cd ../storage_test_plugin
|
|
||||||
docker run --rm -v ${PWD}:/project builder make
|
|
||||||
- uses: actions/upload-artifact@master
|
- uses: actions/upload-artifact@master
|
||||||
with:
|
with:
|
||||||
name: binary
|
name: binary
|
||||||
|
14
.github/workflows/push_image.yml
vendored
14
.github/workflows/push_image.yml
vendored
@ -3,7 +3,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- '*-dev'
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
@ -32,9 +31,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: clang-format
|
- name: clang-format
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/storage_test_plugin/src --exclude ./plugins/storage_test_plugin/src/catch2
|
|
||||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin/src
|
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin/src
|
||||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./plugins/example_plugin_cpp/src
|
|
||||||
build-examples:
|
build-examples:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: clang-format-examples
|
needs: clang-format-examples
|
||||||
@ -45,10 +42,6 @@ jobs:
|
|||||||
docker build . -f Dockerfile.buildexamples -t builder
|
docker build . -f Dockerfile.buildexamples -t builder
|
||||||
cd ./plugins/example_plugin
|
cd ./plugins/example_plugin
|
||||||
docker run --rm -v ${PWD}:/project builder make
|
docker run --rm -v ${PWD}:/project builder make
|
||||||
cd ../example_plugin_cpp
|
|
||||||
docker run --rm -v ${PWD}:/project builder make
|
|
||||||
cd ../storage_test_plugin
|
|
||||||
docker run --rm -v ${PWD}:/project builder make
|
|
||||||
build-and-push-image:
|
build-and-push-image:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build-lib, build-examples]
|
needs: [build-lib, build-examples]
|
||||||
@ -65,10 +58,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value={{branch}}-{{date 'YYYYMMDD'}}-{{sha}},enable=${{ github.ref != format('refs/heads/{0}', 'main') }}
|
type=raw,value={{date 'YYYYMMDD'}}-{{sha}}
|
||||||
type=raw,value={{date 'YYYYMMDD'}}-{{sha}},enable={{is_default_branch}}
|
type=raw,value={{date 'YYYYMMDD'}}
|
||||||
type=raw,value={{date 'YYYYMMDD'}},enable={{is_default_branch}}
|
type=raw,value=latest
|
||||||
type=raw,value=latest,enable={{is_default_branch}}
|
|
||||||
|
|
||||||
- name: Log into registry ${{ env.REGISTRY }}
|
- name: Log into registry ${{ env.REGISTRY }}
|
||||||
uses: docker/login-action@v2.1.0
|
uses: docker/login-action@v2.1.0
|
||||||
|
18
Makefile
18
Makefile
@ -2,8 +2,8 @@ TOPDIR ?= $(CURDIR)
|
|||||||
include $(TOPDIR)/share/wups_rules
|
include $(TOPDIR)/share/wups_rules
|
||||||
|
|
||||||
export WUPS_MAJOR := 0
|
export WUPS_MAJOR := 0
|
||||||
export WUPS_MINOR := 8
|
export WUPS_MINOR := 7
|
||||||
export WUPS_PATCH := 0
|
export WUPS_PATCH := 1
|
||||||
|
|
||||||
VERSION := $(WUPS_MAJOR).$(WUPS_MINOR).$(WUPS_PATCH)
|
VERSION := $(WUPS_MAJOR).$(WUPS_MINOR).$(WUPS_PATCH)
|
||||||
|
|
||||||
@ -16,21 +16,23 @@ VERSION := $(WUPS_MAJOR).$(WUPS_MINOR).$(WUPS_PATCH)
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
TARGET := wups
|
TARGET := wups
|
||||||
#BUILD := build
|
#BUILD := build
|
||||||
SOURCES := libraries/libwups/
|
SOURCES := libraries/libwups/ \
|
||||||
|
libraries/libwups/utils
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES := include
|
INCLUDES := include
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
CFLAGS := -g -O2 -Wall -Werror -save-temps \
|
CFLAGS := -g -Wall -Werror -save-temps \
|
||||||
-ffunction-sections -fdata-sections \
|
-ffunction-sections -fdata-sections \
|
||||||
|
-fno-exceptions -fno-rtti \
|
||||||
$(MACHDEP) \
|
$(MACHDEP) \
|
||||||
$(BUILD_CFLAGS)
|
$(BUILD_CFLAGS)
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
|
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -std=c++20
|
CXXFLAGS := $(CFLAGS) -std=gnu++20
|
||||||
|
|
||||||
ASFLAGS := -g $(MACHDEP)
|
ASFLAGS := -g $(MACHDEP)
|
||||||
|
|
||||||
@ -38,7 +40,7 @@ ASFLAGS := -g $(MACHDEP)
|
|||||||
# list of directories containing libraries, this must be the top level containing
|
# list of directories containing libraries, this must be the top level containing
|
||||||
# include and lib
|
# include and lib
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
LIBDIRS := $(WUT_ROOT)
|
LIBDIRS :=
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# no real need to edit anything past this point unless you need to add additional
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
@ -102,10 +104,10 @@ lib:
|
|||||||
@[ -d $@ ] || mkdir -p $@
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
|
||||||
release:
|
release:
|
||||||
@$(shell [ ! -d 'release' ] && mkdir -p 'release')
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
@$(shell [ ! -d 'debug' ] && mkdir -p 'debug')
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
|
||||||
lib/libwups.a : lib release $(SOURCES) $(INCLUDES)
|
lib/libwups.a : lib release $(SOURCES) $(INCLUDES)
|
||||||
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
|
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
|
||||||
|
@ -28,4 +28,4 @@ It's highly recommended to pin the version to the **latest date** instead of usi
|
|||||||
|
|
||||||
## Format the code via docker
|
## Format the code via docker
|
||||||
|
|
||||||
`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./include ./libraries ./plugins/example_plugin/src ./plugins/example_plugin_cpp/src ./plugins/storage_test_plugin/src --exclude ./plugins/storage_test_plugin/src/catch2 -i`
|
`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./include ./libraries ./plugins/example_plugin/src -i`
|
||||||
|
@ -27,9 +27,8 @@
|
|||||||
|
|
||||||
#include "wups/common.h"
|
#include "wups/common.h"
|
||||||
#include "wups/config.h"
|
#include "wups/config.h"
|
||||||
#include "wups/config_api.h"
|
#include "wups/config_imports.h"
|
||||||
#include "wups/function_patching.h"
|
#include "wups/function_patching.h"
|
||||||
#include "wups/hooks.h"
|
#include "wups/hooks.h"
|
||||||
#include "wups/meta.h"
|
#include "wups/meta.h"
|
||||||
#include "wups/storage.h"
|
#include "wups/storage.h"
|
||||||
#include "wups/wups_debug.h"
|
|
||||||
|
@ -17,74 +17,26 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <padscore/kpad.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <vpad/input.h>
|
|
||||||
|
|
||||||
typedef uint32_t WUPSConfigButtons;
|
#define WUPS_CONFIG_BUTTON_NONE 0
|
||||||
|
#define WUPS_CONFIG_BUTTON_LEFT (1 << 0)
|
||||||
typedef enum WUPS_CONFIG_SIMPLE_INPUT {
|
#define WUPS_CONFIG_BUTTON_RIGHT (1 << 1)
|
||||||
WUPS_CONFIG_BUTTON_NONE = 0,
|
#define WUPS_CONFIG_BUTTON_UP (1 << 2)
|
||||||
WUPS_CONFIG_BUTTON_LEFT = (1 << 0),
|
#define WUPS_CONFIG_BUTTON_DOWN (1 << 3)
|
||||||
WUPS_CONFIG_BUTTON_RIGHT = (1 << 1),
|
#define WUPS_CONFIG_BUTTON_A (1 << 4)
|
||||||
WUPS_CONFIG_BUTTON_UP = (1 << 2),
|
#define WUPS_CONFIG_BUTTON_B (1 << 5)
|
||||||
WUPS_CONFIG_BUTTON_DOWN = (1 << 3),
|
#define WUPS_CONFIG_BUTTON_ZL (1 << 6)
|
||||||
WUPS_CONFIG_BUTTON_A = (1 << 4),
|
#define WUPS_CONFIG_BUTTON_ZR (1 << 7)
|
||||||
WUPS_CONFIG_BUTTON_B = (1 << 5),
|
#define WUPS_CONFIG_BUTTON_L (1 << 8)
|
||||||
WUPS_CONFIG_BUTTON_ZL = (1 << 6),
|
#define WUPS_CONFIG_BUTTON_R (1 << 9)
|
||||||
WUPS_CONFIG_BUTTON_ZR = (1 << 7),
|
#define WUPS_CONFIG_BUTTON_X (1 << 10)
|
||||||
WUPS_CONFIG_BUTTON_L = (1 << 8),
|
#define WUPS_CONFIG_BUTTON_Y (1 << 11)
|
||||||
WUPS_CONFIG_BUTTON_R = (1 << 9),
|
#define WUPS_CONFIG_BUTTON_STICK_L (1 << 12)
|
||||||
WUPS_CONFIG_BUTTON_X = (1 << 10),
|
#define WUPS_CONFIG_BUTTON_STICK_R (1 << 13)
|
||||||
WUPS_CONFIG_BUTTON_Y = (1 << 11),
|
#define WUPS_CONFIG_BUTTON_PLUS (1 << 14)
|
||||||
WUPS_CONFIG_BUTTON_STICK_L = (1 << 12),
|
#define WUPS_CONFIG_BUTTON_MINUS (1 << 15)
|
||||||
WUPS_CONFIG_BUTTON_STICK_R = (1 << 13),
|
typedef int32_t WUPSConfigButtons;
|
||||||
WUPS_CONFIG_BUTTON_PLUS = (1 << 14),
|
|
||||||
WUPS_CONFIG_BUTTON_MINUS = (1 << 15),
|
|
||||||
} WUPS_CONFIG_SIMPLE_INPUT;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
WUPS_CONFIG_SIMPLE_INPUT buttons_h;
|
|
||||||
WUPS_CONFIG_SIMPLE_INPUT buttons_d;
|
|
||||||
WUPS_CONFIG_SIMPLE_INPUT buttons_r;
|
|
||||||
bool validPointer;
|
|
||||||
bool touched;
|
|
||||||
float pointerAngle;
|
|
||||||
int32_t x;
|
|
||||||
int32_t y;
|
|
||||||
} WUPSConfigSimplePadData;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
struct {
|
|
||||||
VPADReadError vpadError;
|
|
||||||
VPADTouchData tpCalib;
|
|
||||||
VPADStatus data;
|
|
||||||
} vpad;
|
|
||||||
struct {
|
|
||||||
KPADError kpadError[4];
|
|
||||||
KPADStatus data[4];
|
|
||||||
} kpad;
|
|
||||||
} WUPSConfigComplexPadData;
|
|
||||||
|
|
||||||
typedef enum WUPSConfigAPIStatus {
|
|
||||||
WUPSCONFIG_API_RESULT_SUCCESS = 0,
|
|
||||||
WUPSCONFIG_API_RESULT_INVALID_ARGUMENT = -0x01,
|
|
||||||
WUPSCONFIG_API_RESULT_OUT_OF_MEMORY = -0x03,
|
|
||||||
WUPSCONFIG_API_RESULT_NOT_FOUND = -0x06,
|
|
||||||
WUPSCONFIG_API_RESULT_INVALID_PLUGIN_IDENTIFIER = -0x70,
|
|
||||||
WUPSCONFIG_API_RESULT_MISSING_CALLBACK = -0x71,
|
|
||||||
WUPSCONFIG_API_RESULT_MODULE_NOT_FOUND = -0x80,
|
|
||||||
WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT = -0x81,
|
|
||||||
WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION = -0x82,
|
|
||||||
WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND = -0x83,
|
|
||||||
WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED = -0x84,
|
|
||||||
WUPSCONFIG_API_RESULT_UNKNOWN_ERROR = -0x100,
|
|
||||||
} WUPSConfigAPIStatus;
|
|
||||||
|
|
||||||
typedef enum WUPSConfigAPICallbackStatus {
|
|
||||||
WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS = 0,
|
|
||||||
WUPSCONFIG_API_CALLBACK_RESULT_ERROR = -1,
|
|
||||||
} WUPSConfigAPICallbackStatus;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t (*getCurrentValueDisplay)(void *context, char *out_buf, int32_t out_size);
|
int32_t (*getCurrentValueDisplay)(void *context, char *out_buf, int32_t out_size);
|
||||||
@ -102,195 +54,8 @@ typedef struct {
|
|||||||
void (*onButtonPressed)(void *context, WUPSConfigButtons button);
|
void (*onButtonPressed)(void *context, WUPSConfigButtons button);
|
||||||
|
|
||||||
void (*onDelete)(void *context);
|
void (*onDelete)(void *context);
|
||||||
} WUPSConfigAPIItemCallbacksV1;
|
} WUPSConfigCallbacks_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef uint32_t WUPSConfigItemHandle;
|
||||||
/**
|
typedef uint32_t WUPSConfigHandle;
|
||||||
* Set the string which is displayed for an item
|
typedef uint32_t WUPSConfigCategoryHandle;
|
||||||
* @param context The context which has been passed to the item during creating
|
|
||||||
* @param out_buf Buffer where the string should be written to
|
|
||||||
* @param out_size Size of out_buf
|
|
||||||
*
|
|
||||||
* \result non-zero result indicates an error.
|
|
||||||
*/
|
|
||||||
int32_t (*getCurrentValueDisplay)(void *context, char *out_buf, int32_t out_size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the string which is displayed for an item when the cursor is on is item
|
|
||||||
* @param context The context which has been passed to the item during creating
|
|
||||||
* @param out_buf Buffer where the string should be written to
|
|
||||||
* @param out_size Size of out_buf
|
|
||||||
*
|
|
||||||
* \result non-zero result indicates an error.
|
|
||||||
*/
|
|
||||||
int32_t (*getCurrentValueSelectedDisplay)(void *context, char *out_buf, int32_t out_size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the cursor enters or leaves this item
|
|
||||||
* @param context The context which has been passed to the item during creating
|
|
||||||
* @param isSelected True if the item cursor is now pointing to this item, \n
|
|
||||||
* False if it's not pointing to this item anymore
|
|
||||||
*/
|
|
||||||
void (*onSelected)(void *context, bool isSelected);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the current value of this item should be set to the default value
|
|
||||||
* @param context The context which has been passed to the item during creating
|
|
||||||
*/
|
|
||||||
void (*restoreDefault)(void *context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if movement to different item is allowed.
|
|
||||||
* @param context The context which has been passed to the item during creating
|
|
||||||
* \return True if it should be not possible to select a different item or exit the current category \n
|
|
||||||
* False if it should be possible to select a different item or exit the current category
|
|
||||||
*/
|
|
||||||
bool (*isMovementAllowed)(void *context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the config menu has been closed
|
|
||||||
* @param context The context which has been passed to the item during creating
|
|
||||||
*/
|
|
||||||
void (*onCloseCallback)(void *context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is called on each frame and provides information about the current inputs.
|
|
||||||
* The inputs are simplified and all 5 possible controller inputs (from Gamepad and up to 4 Wiimotes/Pro Controller)
|
|
||||||
* are unified in this single unified struct.
|
|
||||||
*
|
|
||||||
* @param context The context which has been passed to the item during creating
|
|
||||||
* @param input Simplified version of the current inputs
|
|
||||||
*
|
|
||||||
* \note To get the full input for all possible controllers see "onInputEx"
|
|
||||||
*
|
|
||||||
* @see onInputEx
|
|
||||||
*/
|
|
||||||
void (*onInput)(void *context, WUPSConfigSimplePadData input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is called on each frame and provides information about the current inputs.
|
|
||||||
* The structs contains information for current individual Gampepad and Wiimote/Pro Contoller inputs.
|
|
||||||
*
|
|
||||||
* @param context The context which has been passed to the item during creating
|
|
||||||
* @param input current input for all possibles controller
|
|
||||||
*
|
|
||||||
* \note To get a simplified input callback that combines all controller into a single struct see "onInput"
|
|
||||||
*
|
|
||||||
* @see onInput
|
|
||||||
*/
|
|
||||||
void (*onInputEx)(void *context, WUPSConfigComplexPadData input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is called when the item is about to be deleted. It can be used to free any alloated memory.
|
|
||||||
*
|
|
||||||
* @param context The context which has been passed to the item during creating
|
|
||||||
*/
|
|
||||||
void (*onDelete)(void *context);
|
|
||||||
} WUPSConfigAPIItemCallbacksV2;
|
|
||||||
|
|
||||||
#define WUPS_API_ITEM_OPTION_VERSION_V1 1
|
|
||||||
#define WUPS_API_ITEM_OPTION_VERSION_V2 2
|
|
||||||
|
|
||||||
typedef struct WUPSConfigAPIItemOptionsV1 {
|
|
||||||
const char *configId;
|
|
||||||
const char *displayName;
|
|
||||||
void *context;
|
|
||||||
WUPSConfigAPIItemCallbacksV1 callbacks;
|
|
||||||
} WUPSConfigAPIItemOptionsV1;
|
|
||||||
|
|
||||||
typedef struct WUPSConfigAPIItemOptionsV2 {
|
|
||||||
const char *displayName;
|
|
||||||
void *context;
|
|
||||||
WUPSConfigAPIItemCallbacksV2 callbacks;
|
|
||||||
} WUPSConfigAPIItemOptionsV2;
|
|
||||||
|
|
||||||
typedef struct WUPSConfigAPICreateItemOptions {
|
|
||||||
uint32_t version;
|
|
||||||
union {
|
|
||||||
WUPSConfigAPIItemOptionsV1 v1;
|
|
||||||
WUPSConfigAPIItemOptionsV2 v2;
|
|
||||||
} data;
|
|
||||||
} WUPSConfigAPICreateItemOptions;
|
|
||||||
|
|
||||||
typedef uint32_t WUPSConfigAPIVersion;
|
|
||||||
|
|
||||||
typedef struct WUPSConfigItemHandle {
|
|
||||||
void *handle;
|
|
||||||
#ifdef __cplusplus
|
|
||||||
WUPSConfigItemHandle() {
|
|
||||||
handle = nullptr;
|
|
||||||
}
|
|
||||||
explicit WUPSConfigItemHandle(void *handle) : handle(handle) {}
|
|
||||||
bool operator==(const WUPSConfigItemHandle other) const {
|
|
||||||
return handle == other.handle;
|
|
||||||
}
|
|
||||||
bool operator==(const void *other) const {
|
|
||||||
return handle == other;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} WUPSConfigItemHandle;
|
|
||||||
|
|
||||||
typedef struct WUPSConfigHandle {
|
|
||||||
void *handle;
|
|
||||||
#ifdef __cplusplus
|
|
||||||
WUPSConfigHandle() {
|
|
||||||
handle = nullptr;
|
|
||||||
}
|
|
||||||
explicit WUPSConfigHandle(void *handle) : handle(handle) {}
|
|
||||||
bool operator==(const WUPSConfigHandle other) const {
|
|
||||||
return handle == other.handle;
|
|
||||||
}
|
|
||||||
bool operator==(const void *other) const {
|
|
||||||
return handle == other;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} WUPSConfigHandle;
|
|
||||||
|
|
||||||
typedef struct WUPSConfigCategoryHandle {
|
|
||||||
void *handle;
|
|
||||||
#ifdef __cplusplus
|
|
||||||
WUPSConfigCategoryHandle() {
|
|
||||||
handle = nullptr;
|
|
||||||
}
|
|
||||||
explicit WUPSConfigCategoryHandle(void *handle) : handle(handle) {}
|
|
||||||
bool operator==(const WUPSConfigCategoryHandle other) const {
|
|
||||||
return handle == other.handle;
|
|
||||||
}
|
|
||||||
bool operator==(const void *other) const {
|
|
||||||
return handle == other;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} WUPSConfigCategoryHandle;
|
|
||||||
|
|
||||||
#define WUPS_API_CATEGORY_OPTION_VERSION_V1 1
|
|
||||||
|
|
||||||
typedef struct WUPSConfigAPICreateCategoryOptionsV1 {
|
|
||||||
const char *name;
|
|
||||||
} WUPSConfigAPICreateCategoryOptionsV1;
|
|
||||||
|
|
||||||
typedef struct WUPSConfigAPICreateCategoryOptions {
|
|
||||||
uint32_t version;
|
|
||||||
union {
|
|
||||||
WUPSConfigAPICreateCategoryOptionsV1 v1;
|
|
||||||
} data;
|
|
||||||
} WUPSConfigAPICreateCategoryOptions;
|
|
||||||
|
|
||||||
#define WUPS_API_CONFIG_API_OPTION_VERSION_V1 1
|
|
||||||
|
|
||||||
typedef struct WUPSConfigAPIOptionsV1 {
|
|
||||||
const char *name;
|
|
||||||
} WUPSConfigAPIOptionsV1;
|
|
||||||
|
|
||||||
typedef struct WUPSConfigAPIOptions {
|
|
||||||
uint32_t version;
|
|
||||||
union {
|
|
||||||
WUPSConfigAPIOptionsV1 v1;
|
|
||||||
} data;
|
|
||||||
} WUPSConfigAPIOptions;
|
|
||||||
|
|
||||||
#define WUPS_CONFIG_API_VERSION_ERROR 0xFFFFFFFF
|
|
||||||
|
|
||||||
typedef struct wups_loader_init_config_args_t {
|
|
||||||
uint32_t arg_version;
|
|
||||||
uint32_t plugin_identifier;
|
|
||||||
} wups_loader_init_config_args_t;
|
|
@ -1,52 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
#include "WUPSConfigItem.h"
|
|
||||||
#include <optional>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class WUPSConfigCategory {
|
|
||||||
public:
|
|
||||||
explicit WUPSConfigCategory(WUPSConfigCategoryHandle handle) noexcept;
|
|
||||||
virtual ~WUPSConfigCategory();
|
|
||||||
|
|
||||||
WUPSConfigCategory(const WUPSConfigCategory &) = delete;
|
|
||||||
|
|
||||||
WUPSConfigCategory(WUPSConfigCategory &&src) noexcept : mHandle(src.mHandle) {
|
|
||||||
src.mHandle = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigCategory &operator=(WUPSConfigCategory &&src) noexcept {
|
|
||||||
if (this != &src) {
|
|
||||||
this->mHandle = src.mHandle;
|
|
||||||
src.mHandle = {};
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::optional<WUPSConfigCategory> Create(std::string_view name, WUPSConfigAPIStatus &error) noexcept;
|
|
||||||
|
|
||||||
static WUPSConfigCategory Create(std::string_view name);
|
|
||||||
|
|
||||||
bool add(WUPSConfigCategory &&cat, WUPSConfigAPIStatus &error) noexcept;
|
|
||||||
|
|
||||||
void add(WUPSConfigCategory &&cat);
|
|
||||||
|
|
||||||
bool add(WUPSConfigItem &&item, WUPSConfigAPIStatus &error) noexcept;
|
|
||||||
|
|
||||||
void add(WUPSConfigItem &&item);
|
|
||||||
|
|
||||||
[[nodiscard]] const WUPSConfigCategoryHandle &getHandle() const {
|
|
||||||
return mHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void release() {
|
|
||||||
mHandle = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
WUPSConfigCategoryHandle mHandle = {};
|
|
||||||
};
|
|
||||||
#endif
|
|
@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
#include "wups/config.h"
|
|
||||||
#include <coreinit/debug.h>
|
|
||||||
|
|
||||||
class WUPSConfigItem {
|
|
||||||
protected:
|
|
||||||
explicit WUPSConfigItem(WUPSConfigItemHandle itemHandle) : mHandle(itemHandle) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~WUPSConfigItem();
|
|
||||||
|
|
||||||
WUPSConfigItem(const WUPSConfigItem &) = delete;
|
|
||||||
|
|
||||||
WUPSConfigItem(WUPSConfigItem &&src) noexcept : mHandle(src.mHandle) {
|
|
||||||
src.mHandle = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigItem &operator=(WUPSConfigItem &&src) noexcept {
|
|
||||||
if (this != &src) {
|
|
||||||
this->mHandle = src.mHandle;
|
|
||||||
src.mHandle = {};
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] const WUPSConfigItemHandle &getHandle() const {
|
|
||||||
return mHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void release() {
|
|
||||||
mHandle = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
WUPSConfigItemHandle mHandle = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,131 +1,41 @@
|
|||||||
#pragma once
|
#include <wups.h>
|
||||||
|
|
||||||
#include <wups/config.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct ConfigItemBoolean {
|
typedef struct ConfigItemBoolean {
|
||||||
|
char *configId;
|
||||||
WUPSConfigItemHandle handle;
|
WUPSConfigItemHandle handle;
|
||||||
const char *identifier;
|
|
||||||
bool defaultValue;
|
bool defaultValue;
|
||||||
bool valueAtCreation;
|
|
||||||
bool value;
|
bool value;
|
||||||
char trueValue[32];
|
char trueValue[32];
|
||||||
char falseValue[32];
|
char falseValue[32];
|
||||||
void *valueChangedCallback;
|
void *callback;
|
||||||
} ConfigItemBoolean;
|
} ConfigItemBoolean;
|
||||||
|
|
||||||
typedef void (*BooleanValueChangedCallback)(ConfigItemBoolean *, bool);
|
typedef void (*BooleanValueChangedCallback)(ConfigItemBoolean *, bool);
|
||||||
|
|
||||||
WUPSConfigAPIStatus
|
bool WUPSConfigItemBoolean_AddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, bool defaultValue, BooleanValueChangedCallback callback);
|
||||||
WUPSConfigItemBoolean_CreateEx(const char *identifier,
|
bool WUPSConfigItemBoolean_AddToCategoryEx(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, bool defaultValue, BooleanValueChangedCallback callback, const char *trueValue,
|
||||||
const char *displayName,
|
const char *falseValue);
|
||||||
bool defaultValue,
|
|
||||||
bool currentValue,
|
|
||||||
BooleanValueChangedCallback callback,
|
|
||||||
const char *trueValue,
|
|
||||||
const char *falseValue,
|
|
||||||
WUPSConfigItemHandle *outHandle);
|
|
||||||
|
|
||||||
|
#define WUPSConfigItemBoolean_AddToCategoryHandled(__config__, __cat__, __configIs__, __displayName__, __defaultValue__, __callback__) \
|
||||||
|
do { \
|
||||||
|
if (!WUPSConfigItemBoolean_AddToCategory(__cat__, __configIs__, __displayName__, __defaultValue__, __callback__)) { \
|
||||||
|
WUPSConfig_Destroy(__config__); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/**
|
#define WUPSConfigItemBoolean_AddToCategoryHandledEx(__config__, __cat__, __configID__, __displayName__, __defaultValue__, __callback__, __trueValue__, __falseValue__) \
|
||||||
* @brief Adds a boolean configuration item to the specified category.
|
do { \
|
||||||
*
|
if (!WUPSConfigItemBoolean_AddToCategoryEx(__cat__, __configID__, __displayName__, __defaultValue__, __callback__, __trueValue__, __falseValue__)) { \
|
||||||
* This function adds a boolean configuration item to the given category. The item is displayed with a specified display name.
|
WUPSConfig_Destroy(__config__); \
|
||||||
* The default value and current value of the item are set to the provided values. A callback function is called whenever
|
return 0; \
|
||||||
* the value of the item changes.
|
} \
|
||||||
*
|
} while (0)
|
||||||
* @param cat The handle of the category to add the item to.
|
|
||||||
* @param identifier Optional identifier for the item. Can be NULL.
|
|
||||||
* @param displayName The display name of the item.
|
|
||||||
* @param defaultValue The default value of the item.
|
|
||||||
* @param currentValue The current value of the item.
|
|
||||||
* @param callback A callback function that will be called when the config menu closes and the value of the item has been changed.
|
|
||||||
* @return True if the item was added successfully, false otherwise.
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus
|
|
||||||
WUPSConfigItemBoolean_AddToCategory(WUPSConfigCategoryHandle cat,
|
|
||||||
const char *identifier,
|
|
||||||
const char *displayName,
|
|
||||||
bool defaultValue,
|
|
||||||
bool currentValue,
|
|
||||||
BooleanValueChangedCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adds a boolean configuration item to the specified category.
|
|
||||||
*
|
|
||||||
* This function adds a boolean configuration item to the given category. The item is displayed with a specified display name.
|
|
||||||
* The default value and current value of the item are set to the provided values. A callback function is called whenever
|
|
||||||
* the value of the item changes.
|
|
||||||
*
|
|
||||||
* @param cat The handle of the category to add the item to.
|
|
||||||
* @param identifier Optional identifier for the item. Can be NULL.
|
|
||||||
* @param displayName The display name of the item.
|
|
||||||
* @param defaultValue The default value of the item.
|
|
||||||
* @param currentValue The current value of the item.
|
|
||||||
* @param callback A callback function that will be called when the config menu closes and the value of the item has been changed.
|
|
||||||
* @param trueValue The string representation of the true value.
|
|
||||||
* @param falseValue The string representation of the false value.
|
|
||||||
* @return True if the item was successfully added to the category, false otherwise.
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus
|
|
||||||
WUPSConfigItemBoolean_AddToCategoryEx(WUPSConfigCategoryHandle cat,
|
|
||||||
const char *identifier,
|
|
||||||
const char *displayName,
|
|
||||||
bool defaultValue,
|
|
||||||
bool currentValue,
|
|
||||||
BooleanValueChangedCallback callback,
|
|
||||||
const char *trueValue,
|
|
||||||
const char *falseValue);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
#include "WUPSConfigItem.h"
|
|
||||||
#include <optional>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <wups/config_api.h>
|
|
||||||
|
|
||||||
class WUPSConfigItemBoolean : public WUPSConfigItem {
|
|
||||||
public:
|
|
||||||
static std::optional<WUPSConfigItemBoolean> CreateEx(std::optional<std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
bool defaultValue,
|
|
||||||
bool currentValue,
|
|
||||||
BooleanValueChangedCallback callback,
|
|
||||||
std::string_view trueValue,
|
|
||||||
std::string_view falseValue,
|
|
||||||
WUPSConfigAPIStatus &err) noexcept;
|
|
||||||
|
|
||||||
static WUPSConfigItemBoolean CreateEx(std::optional<std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
bool defaultValue,
|
|
||||||
bool currentValue,
|
|
||||||
BooleanValueChangedCallback callback,
|
|
||||||
std::string_view trueValue,
|
|
||||||
std::string_view falseValue);
|
|
||||||
|
|
||||||
static std::optional<WUPSConfigItemBoolean> Create(std::optional<std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
bool defaultValue,
|
|
||||||
bool currentValue,
|
|
||||||
BooleanValueChangedCallback callback,
|
|
||||||
WUPSConfigAPIStatus &err) noexcept;
|
|
||||||
|
|
||||||
static WUPSConfigItemBoolean Create(std::optional<std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
bool defaultValue,
|
|
||||||
bool currentValue,
|
|
||||||
BooleanValueChangedCallback callback);
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit WUPSConfigItemBoolean(WUPSConfigItemHandle itemHandle) : WUPSConfigItem(itemHandle) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
#endif
|
@ -1,92 +1,33 @@
|
|||||||
#pragma once
|
#include <wups.h>
|
||||||
|
|
||||||
#include <wups/config.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct ConfigItemIntegerRange {
|
typedef struct ConfigItemIntegerRange {
|
||||||
|
char *configId;
|
||||||
WUPSConfigItemHandle handle;
|
WUPSConfigItemHandle handle;
|
||||||
const char *identifier;
|
|
||||||
int defaultValue;
|
int defaultValue;
|
||||||
int value;
|
int value;
|
||||||
int valueAtCreation;
|
|
||||||
int minValue;
|
int minValue;
|
||||||
int maxValue;
|
int maxValue;
|
||||||
void *valueChangedCallback;
|
void *callback;
|
||||||
} ConfigItemIntegerRange;
|
} ConfigItemIntegerRange;
|
||||||
|
|
||||||
typedef void (*IntegerRangeValueChangedCallback)(ConfigItemIntegerRange *, int32_t);
|
typedef void (*IntegerRangeValueChangedCallback)(ConfigItemIntegerRange *, int32_t);
|
||||||
|
|
||||||
WUPSConfigAPIStatus
|
bool WUPSConfigItemIntegerRange_AddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName,
|
||||||
WUPSConfigItemIntegerRange_Create(const char *identifier,
|
int32_t defaultValue, int32_t minValue, int32_t maxValue,
|
||||||
const char *displayName,
|
IntegerRangeValueChangedCallback callback);
|
||||||
int32_t defaultValue, int32_t currentValue,
|
|
||||||
int32_t minValue, int32_t maxValue,
|
|
||||||
IntegerRangeValueChangedCallback callback,
|
|
||||||
WUPSConfigItemHandle *outHandle);
|
|
||||||
|
|
||||||
/**
|
#define WUPSConfigItemIntegerRange_AddToCategoryHandled(__config__, __cat__, __configId__, __displayName__, __defaultValue__, __minValue__, __maxValue__, __callback__) \
|
||||||
* \brief Adds an integer range configuration item to a category.
|
do { \
|
||||||
*
|
if (!WUPSConfigItemIntegerRange_AddToCategory(__cat__, __configId__, __displayName__, __defaultValue__, __minValue__, __maxValue__, __callback__)) { \
|
||||||
* This function creates a new ConfigItemIntegerRange item and adds it to the specified category.
|
WUPSConfig_Destroy(__config__); \
|
||||||
* The item represents an integer value within a specified range, and allows the user to modify the value.
|
return 0; \
|
||||||
*
|
} \
|
||||||
* \param cat The category handle to which the item should be added.
|
} while (0)
|
||||||
* \param identifier Optional identifier for the item. Can be NULL.
|
|
||||||
* \param displayName The display name for the item.
|
|
||||||
* \param defaultValue The default value for the item.
|
|
||||||
* \param currentValue The current value for the item.
|
|
||||||
* \param minValue The minimum value allowed for the item.
|
|
||||||
* \param maxValue The maximum value allowed for the item.
|
|
||||||
* \param callback A callback function that will be called when the config menu closes and the value of the item has been changed.
|
|
||||||
*
|
|
||||||
* \return Returns true if the item was successfully added to the category, false otherwise.
|
|
||||||
*
|
|
||||||
* @note The defaultValue and currentValue must in the specified range.
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus
|
|
||||||
WUPSConfigItemIntegerRange_AddToCategory(WUPSConfigCategoryHandle cat,
|
|
||||||
const char *identifier,
|
|
||||||
const char *displayName,
|
|
||||||
int32_t defaultValue, int32_t currentValue,
|
|
||||||
int32_t minValue, int32_t maxValue,
|
|
||||||
IntegerRangeValueChangedCallback callback);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#include "WUPSConfigItem.h"
|
|
||||||
#include <optional>
|
|
||||||
#include <span>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <wups/config_api.h>
|
|
||||||
|
|
||||||
class WUPSConfigItemIntegerRange : public WUPSConfigItem {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static std::optional<WUPSConfigItemIntegerRange> Create(
|
|
||||||
std::optional<std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
int32_t defaultValue, int32_t currentValue,
|
|
||||||
int32_t minValue, int32_t maxValue,
|
|
||||||
IntegerRangeValueChangedCallback valuesChangedCallback,
|
|
||||||
WUPSConfigAPIStatus &err) noexcept;
|
|
||||||
|
|
||||||
static WUPSConfigItemIntegerRange Create(
|
|
||||||
std::optional<std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
int32_t defaultValue, int32_t currentValue,
|
|
||||||
int32_t minValue, int32_t maxValue,
|
|
||||||
IntegerRangeValueChangedCallback valuesChangedCallback);
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit WUPSConfigItemIntegerRange(WUPSConfigItemHandle itemHandle) : WUPSConfigItem(itemHandle) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
@ -1,8 +1,4 @@
|
|||||||
#pragma once
|
#include <wups.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <wups/config.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -10,107 +6,32 @@ extern "C" {
|
|||||||
|
|
||||||
typedef struct ConfigItemMultipleValuesPair {
|
typedef struct ConfigItemMultipleValuesPair {
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
const char *valueName;
|
char *valueName;
|
||||||
} ConfigItemMultipleValuesPair;
|
} ConfigItemMultipleValuesPair;
|
||||||
|
|
||||||
typedef struct ConfigItemMultipleValues {
|
typedef struct ConfigItemMultipleValues {
|
||||||
|
char *configId;
|
||||||
WUPSConfigItemHandle handle;
|
WUPSConfigItemHandle handle;
|
||||||
const char *identifier;
|
|
||||||
int32_t defaultValueIndex;
|
int32_t defaultValueIndex;
|
||||||
int32_t valueIndex;
|
int32_t valueIndex;
|
||||||
int32_t valueIndexAtCreation;
|
void *callback;
|
||||||
ConfigItemMultipleValuesPair *values;
|
ConfigItemMultipleValuesPair *values;
|
||||||
int valueCount;
|
int valueCount;
|
||||||
void *valueChangedCallback;
|
|
||||||
} ConfigItemMultipleValues;
|
} ConfigItemMultipleValues;
|
||||||
|
|
||||||
typedef void (*MultipleValuesChangedCallback)(ConfigItemMultipleValues *, uint32_t);
|
typedef void (*MultipleValuesChangedCallback)(ConfigItemMultipleValues *, uint32_t);
|
||||||
|
|
||||||
|
bool WUPSConfigItemMultipleValues_AddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, int defaultValueIndex, ConfigItemMultipleValuesPair *possibleValues,
|
||||||
|
int pairCount, MultipleValuesChangedCallback callback);
|
||||||
|
|
||||||
WUPSConfigAPIStatus
|
#define WUPSConfigItemMultipleValues_AddToCategoryHandled(__config__, __cat__, __configID__, __displayName__, __defaultValueIndex__, __possibleValues__, __pairCount__, __callback__) \
|
||||||
WUPSConfigItemMultipleValues_Create(const char *identifier, const char *displayName,
|
do { \
|
||||||
int defaultValueIndex, int currentValueIndex,
|
if (!WUPSConfigItemMultipleValues_AddToCategory(__cat__, __configID__, __displayName__, __defaultValueIndex__, __possibleValues__, __pairCount__, __callback__)) { \
|
||||||
ConfigItemMultipleValuesPair *possibleValues, int pairCount,
|
WUPSConfig_Destroy(__config__); \
|
||||||
MultipleValuesChangedCallback callback,
|
return 0; \
|
||||||
WUPSConfigItemHandle *outHandle);
|
} \
|
||||||
|
} while (0)
|
||||||
/**
|
|
||||||
* @brief Add a multiple values configuration item to a category.
|
|
||||||
*
|
|
||||||
* This function adds a multiple values configuration item to a specified category.
|
|
||||||
* The item will be displayed in the configuration menu with the provided display name.
|
|
||||||
*
|
|
||||||
* @param cat The handle of the category where the item should be added.
|
|
||||||
* @param identifier The identifier of the item. It is used to uniquely identify the item.
|
|
||||||
* @param displayName The display name of the item. It will be shown in the configuration menu.
|
|
||||||
* @param defaultValueIndex The index of the default value in the array of possible values.
|
|
||||||
* @param currentValueIndex The index of the current value in the array of possible values.
|
|
||||||
* @param possibleValues An array of possible values for the item.
|
|
||||||
* @param pairCount The number of pairs (value and value name) in the possibleValues array.
|
|
||||||
* @param callback A callback function that will be called when the config menu closes and the value of the item has been changed.
|
|
||||||
*
|
|
||||||
* @return true if the item was successfully added to the category, false otherwise.
|
|
||||||
*
|
|
||||||
* @note The defaultValueIndex and currentValueIndex must be valid for the given pairCount.
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus
|
|
||||||
WUPSConfigItemMultipleValues_AddToCategory(WUPSConfigCategoryHandle cat, const char *identifier, const char *displayName,
|
|
||||||
int defaultValueIndex, int currentValueIndex,
|
|
||||||
ConfigItemMultipleValuesPair *possibleValues, int pairCount,
|
|
||||||
MultipleValuesChangedCallback callback);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
#include "WUPSConfigItem.h"
|
|
||||||
#include <optional>
|
|
||||||
#include <span>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <wups/config_api.h>
|
|
||||||
|
|
||||||
class WUPSConfigItemMultipleValues : public WUPSConfigItem {
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct ValuePair {
|
|
||||||
uint32_t value;
|
|
||||||
std::string_view name;
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::optional<WUPSConfigItemMultipleValues> CreateFromIndex(std::optional<const std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
int defaultValueIndex, int currentValueIndex,
|
|
||||||
const std::span<const ValuePair> &possibleValues,
|
|
||||||
MultipleValuesChangedCallback valuesChangedCallback,
|
|
||||||
WUPSConfigAPIStatus &err) noexcept;
|
|
||||||
|
|
||||||
static WUPSConfigItemMultipleValues CreateFromIndex(std::optional<const std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
int defaultValueIndex, int currentValueIndex,
|
|
||||||
const std::span<const ValuePair> &possibleValues,
|
|
||||||
MultipleValuesChangedCallback valuesChangedCallback);
|
|
||||||
|
|
||||||
static std::optional<WUPSConfigItemMultipleValues> CreateFromValue(
|
|
||||||
std::optional<const std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
uint32_t defaultValue, uint32_t currentValue,
|
|
||||||
const std::span<const ValuePair> &possibleValues,
|
|
||||||
MultipleValuesChangedCallback valuesChangedCallback,
|
|
||||||
WUPSConfigAPIStatus &err) noexcept;
|
|
||||||
|
|
||||||
static WUPSConfigItemMultipleValues CreateFromValue(
|
|
||||||
std::optional<const std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
int32_t defaultValue, int32_t currentValue,
|
|
||||||
const std::span<const ValuePair> &possibleValues,
|
|
||||||
MultipleValuesChangedCallback valuesChangedCallback);
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit WUPSConfigItemMultipleValues(WUPSConfigItemHandle itemHandle) : WUPSConfigItem(itemHandle) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
#endif
|
@ -1,6 +1,4 @@
|
|||||||
#pragma once
|
#include <wups.h>
|
||||||
|
|
||||||
#include <wups/config.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -10,39 +8,16 @@ typedef struct ConfigItemStub {
|
|||||||
WUPSConfigItemHandle handle;
|
WUPSConfigItemHandle handle;
|
||||||
} ConfigItemStub;
|
} ConfigItemStub;
|
||||||
|
|
||||||
WUPSConfigAPIStatus WUPSConfigItemStub_Create(const char *displayName,
|
bool WUPSConfigItemStub_AddToCategory(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName);
|
||||||
WUPSConfigItemHandle *outHandle);
|
|
||||||
|
|
||||||
/**
|
#define WUPSConfigItemStub_AddToCategoryHandled(__config__, __cat__, __configID__, __displayName__) \
|
||||||
* @brief Adds a stub item to a category to display information
|
do { \
|
||||||
*
|
if (!WUPSConfigItemStub_AddToCategory(__cat__, __configID__, __displayName__)) { \
|
||||||
* @param cat The handle of the category to which the item should be added.
|
WUPSConfig_Destroy(__config__); \
|
||||||
* @param displayName The display name of the item.
|
return 0; \
|
||||||
* @return true if the item was added successfully, false otherwise.
|
} \
|
||||||
*/
|
} while (0)
|
||||||
WUPSConfigAPIStatus
|
|
||||||
WUPSConfigItemStub_AddToCategory(WUPSConfigCategoryHandle cat, const char *displayName);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
#include "WUPSConfigItem.h"
|
|
||||||
#include <optional>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <wups/config_api.h>
|
|
||||||
|
|
||||||
class WUPSConfigItemStub : public WUPSConfigItem {
|
|
||||||
public:
|
|
||||||
static std::optional<WUPSConfigItemStub> Create(std::string_view displayName,
|
|
||||||
WUPSConfigAPIStatus &err) noexcept;
|
|
||||||
|
|
||||||
static WUPSConfigItemStub Create(std::string_view displayName);
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit WUPSConfigItemStub(WUPSConfigItemHandle itemHandle) : WUPSConfigItem(itemHandle) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
@ -1,213 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "config/WUPSConfigCategory.h"
|
|
||||||
#include "config/WUPSConfigItem.h"
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef WUPSConfigAPICallbackStatus (*WUPSConfigAPI_MenuOpenedCallback)(WUPSConfigCategoryHandle root);
|
|
||||||
typedef void (*WUPSConfigAPI_MenuClosedCallback)();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the WUPSConfigAPI with extended options. For internal use.
|
|
||||||
* @see WUPSConfigAPI_Init instead
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_InitEx(uint32_t pluginIdentifier, WUPSConfigAPIOptions, WUPSConfigAPI_MenuOpenedCallback, WUPSConfigAPI_MenuClosedCallback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initializes the WUPSConfigAPI with the given options, opened callback, and closed callback.
|
|
||||||
*
|
|
||||||
* This function initializes the WUPSConfigAPI with the provided options, opened callback, and closed callback.
|
|
||||||
* If the initialization is successful, it returns WUPSCONFIG_API_RESULT_SUCCESS. Otherwise, it returns
|
|
||||||
* an appropriate error code indicating the reason for failure.
|
|
||||||
*
|
|
||||||
* @param optionsV1 The options for the WUPSConfigAPI. It contains the name of the plugin.
|
|
||||||
* @param openedCallback The callback function to be called when the menu is opened.
|
|
||||||
* @param closedCallback The callback function to be called when the menu is closed.
|
|
||||||
*
|
|
||||||
* @return WUPSConfigAPIStatus WUPSCONFIG_API_RESULT_SUCCESS if initialization is successful,
|
|
||||||
* otherwise an appropriate error code.
|
|
||||||
* - WUPSCONFIG_API_RESULT_SUCCESS: The initialization was successful.
|
|
||||||
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: The `openedCallback` or `closedCallback` parameter is nullptr.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION: The specified `options.version` is not supported.
|
|
||||||
* - WUPSCONFIG_API_RESULT_NOT_FOUND: The plugin with the given identifier was not found
|
|
||||||
* - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The API has not been initialized.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The command is not supported.
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Init(WUPSConfigAPIOptionsV1 optionsV1, WUPSConfigAPI_MenuOpenedCallback openedCallback, WUPSConfigAPI_MenuClosedCallback closedCallback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Retrieves the version of the WUPSConfigAPI library.
|
|
||||||
*
|
|
||||||
* This function retrieves the version of the WUPSConfigAPI library and stores it in the provided output variable.
|
|
||||||
*
|
|
||||||
* @param[out] outVariable Pointer to a WUPSConfigAPIVersion variable to store the library version.
|
|
||||||
* @return Returns the status of the API call.
|
|
||||||
* - WUPSCONFIG_API_RESULT_SUCCESS: The version was retrieved successfully.
|
|
||||||
* - WUPSCONFIG_API_RESULT_MODULE_NOT_FOUND: The module containing the WUPSConfigAPI_GetVersion function was not found.
|
|
||||||
* - WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT: The WUPSConfigAPI_GetVersion function was not found in the module.
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_GetVersion(WUPSConfigAPIVersion *outVariable);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a new category with extended options. Internal use.
|
|
||||||
* @see WUPSConfigAPI_Category_Create
|
|
||||||
**/
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Category_CreateEx(WUPSConfigAPICreateCategoryOptions options, WUPSConfigCategoryHandle *out);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a new category.
|
|
||||||
*
|
|
||||||
* This function creates a new category using the given options. The options struct contains the category name.
|
|
||||||
* If the category creation is successful, the category handle is returned through the *out parameter.
|
|
||||||
*
|
|
||||||
* @param options The options for creating the category.
|
|
||||||
* @param[out] out A pointer to a WUPSConfigCategoryHandle variable that will receive the category handle.
|
|
||||||
* @return WUPSConfigAPIStatus The status of the category creation operation.
|
|
||||||
* - WUPSCONFIG_API_RESULT_SUCCESS: Success.
|
|
||||||
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: Invalid parameter, `out` or `name` is NULL.
|
|
||||||
* - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The API has not been initialized.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The command is not supported.
|
|
||||||
*
|
|
||||||
* @note The function internally calls WUPSConfigAPI_Category_CreateEx().
|
|
||||||
* @note The caller is responsible for deleting the WUPSConfigCategoryHandle instance unless it has been transferred to
|
|
||||||
* a different category
|
|
||||||
*/
|
|
||||||
static inline WUPSConfigAPIStatus WUPSConfigAPI_Category_Create(WUPSConfigAPICreateCategoryOptionsV1 options, WUPSConfigCategoryHandle *out) {
|
|
||||||
WUPSConfigAPICreateCategoryOptions optionsWrapper = {
|
|
||||||
.version = WUPS_API_CATEGORY_OPTION_VERSION_V1,
|
|
||||||
.data = {.v1 = options},
|
|
||||||
};
|
|
||||||
return WUPSConfigAPI_Category_CreateEx(optionsWrapper, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destroys a WUPSConfigCategoryHandle.
|
|
||||||
*
|
|
||||||
* This function is used to destroy a WUPSConfigCategoryHandle object.
|
|
||||||
*
|
|
||||||
* @param handle The WUPSConfigCategoryHandle to be destroyed.
|
|
||||||
* @return WUPSConfigAPIStatus Returns the status of the API call:
|
|
||||||
* - WUPSCONFIG_API_RESULT_SUCCESS: The WUPSConfigCategoryHandle was successfully destroyed.
|
|
||||||
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: The handle is nullptr.
|
|
||||||
* - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The API has not been initialized.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The command is not supported.
|
|
||||||
*
|
|
||||||
* @note The caller is responsible for deleting the WUPSConfigCategoryHandle instance unless it has been transferred to
|
|
||||||
* a different category
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Category_Destroy(WUPSConfigCategoryHandle handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adds a category to the parent category.
|
|
||||||
*
|
|
||||||
* This function is used to add a category to an existing parent category.
|
|
||||||
*
|
|
||||||
* @param parentHandle The handle to the parent category.
|
|
||||||
* @param categoryHandle The handle to the category to be added.
|
|
||||||
*
|
|
||||||
* @return WUPSConfigAPIStatus The status code indicating the result of the operation.
|
|
||||||
* Possible values are:
|
|
||||||
* - WUPSCONFIG_API_RESULT_SUCCESS: The category was added successfully.
|
|
||||||
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: The parentHandle or categoryHandle is null.
|
|
||||||
* - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The library is not initialized.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The command is not supported.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNKNOWN_ERROR: An unknown error occurred.
|
|
||||||
*
|
|
||||||
* @note On success the ownership of the category will be transferred to the Category and the categoryHandle WUPSConfigCategoryHandle
|
|
||||||
* will be invalid.
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Category_AddCategory(WUPSConfigCategoryHandle parentHandle, WUPSConfigCategoryHandle categoryHandle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adds an item to the given category.
|
|
||||||
*
|
|
||||||
* This function adds the specified item to the parent category. The parent
|
|
||||||
* category and item handles must be valid and non-zero.
|
|
||||||
*
|
|
||||||
* @param parentHandle The handle of the parent category.
|
|
||||||
* @param[out] itemHandle The handle of the item to be added.
|
|
||||||
* @return WUPSConfigAPIStatus The status code indicating the result of the operation.
|
|
||||||
* - WUPSCONFIG_API_RESULT_SUCCESS: If the item was added successfully.
|
|
||||||
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: If either the parentHandle or itemHandle is invalid.
|
|
||||||
* - WUPSCONFIG_API_RESULT_NOT_FOUND: If the parent category was not found.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNKNOWN_ERROR: If an unknown error occurred while adding the item.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The provided command is not supported.
|
|
||||||
* - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The library is not initialized properly.
|
|
||||||
* @see WUPSConfigCategoryHandle
|
|
||||||
* @see WUPSConfigItemHandle
|
|
||||||
* @see WUPSConfigAPIStatus
|
|
||||||
*
|
|
||||||
* @note On success the ownership of the item will be transferred to the Category and the itemHandle WUPSConfigItemHandle
|
|
||||||
* will be invalid.
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Category_AddItem(WUPSConfigCategoryHandle parentHandle, WUPSConfigItemHandle itemHandle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a WUPSConfigAPI item with extended options. Internal use.
|
|
||||||
* @see WUPSConfigAPI_Item_Create
|
|
||||||
**/
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Item_CreateEx(WUPSConfigAPICreateItemOptions options, WUPSConfigItemHandle *out);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates a new configuration item using the provided options.
|
|
||||||
*
|
|
||||||
* This function creates a new configuration item using the provided options.
|
|
||||||
*
|
|
||||||
* @param options The options for creating the configuration item.
|
|
||||||
* @param[out] out The handle to the created configuration item.
|
|
||||||
* @return The status of the API call. Possible values are:
|
|
||||||
* - WUPSCONFIG_API_RESULT_SUCCESS if the item was created and added successfully.
|
|
||||||
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT if the 'out' parameter is nullptr.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION if the options version is invalid.
|
|
||||||
* - WUPSCONFIG_API_RESULT_OUT_OF_MEMORY if memory allocation failed.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: The provided command is not supported.
|
|
||||||
* - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: The library is not initialized properly.
|
|
||||||
*
|
|
||||||
* @note The caller is responsible for deleting the WUPSConfigItem instance unless it has been transferred to
|
|
||||||
* a category
|
|
||||||
*/
|
|
||||||
static inline WUPSConfigAPIStatus WUPSConfigAPI_Item_Create(WUPSConfigAPIItemOptionsV2 options, WUPSConfigItemHandle *out) {
|
|
||||||
WUPSConfigAPICreateItemOptions itemOptions = {
|
|
||||||
.version = WUPS_API_ITEM_OPTION_VERSION_V2,
|
|
||||||
.data = {.v2 = options},
|
|
||||||
};
|
|
||||||
return WUPSConfigAPI_Item_CreateEx(itemOptions, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destroy a WUPSConfigItemHandle.
|
|
||||||
*
|
|
||||||
* This function destroys a WUPSConfigItemHandle. It can only be called if the WUPSConfig API is initialized and the handle is valid.
|
|
||||||
* A item must not be destroyed if it's added to a WUPSConfigCategory
|
|
||||||
*
|
|
||||||
* @param handle The handle to be destroyed.
|
|
||||||
* @return WUPSConfigAPIStatus The status code indicating the result of the operation:
|
|
||||||
* - WUPSCONFIG_API_RESULT_SUCCESS: If the handle was successfully destroyed.
|
|
||||||
* - WUPSCONFIG_API_RESULT_NOT_FOUND: The WUPSConfigItem with the given handle was not found.
|
|
||||||
* - WUPSCONFIG_API_RESULT_INVALID_ARGUMENT: If the handle is invalid.
|
|
||||||
* - WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED: If the WUPSConfig API is not initialized.
|
|
||||||
* - WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND: If the destroy command is not supported by the API or the API version is too low.
|
|
||||||
*/
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Item_Destroy(WUPSConfigItemHandle handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Converts a WUPSConfigAPIStatus value to a corresponding string representation.
|
|
||||||
*
|
|
||||||
* This function takes a WUPSConfigAPIStatus value and returns a string representation of the value.
|
|
||||||
* The string representation is determined based on the given status value using a switch statement.
|
|
||||||
* If the status value is not recognized, a default string "WUPSCONFIG_API_RESULT_UNKNOWN_ERROR" is returned.
|
|
||||||
*
|
|
||||||
* @param status The WUPSConfigAPIStatus value to convert to string.
|
|
||||||
* @return The string representation of the given WUPSConfigAPIStatus value.
|
|
||||||
*/
|
|
||||||
const char *WUPSConfigAPI_GetStatusStr(WUPSConfigAPIStatus status);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
63
include/wups/config_imports.h
Normal file
63
include/wups/config_imports.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "stdint.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int32_t WUPSConfigItem_Create(WUPSConfigItemHandle *out, const char *configID, const char *displayName, WUPSConfigCallbacks_t callbacks, void *context);
|
||||||
|
|
||||||
|
int32_t WUPSConfigItem_Destroy(WUPSConfigItemHandle handle);
|
||||||
|
|
||||||
|
int32_t WUPSConfig_Destroy(WUPSConfigHandle handle);
|
||||||
|
|
||||||
|
int32_t WUPSConfigItem_SetDisplayName(WUPSConfigItemHandle handle, const char *displayName);
|
||||||
|
|
||||||
|
int32_t WUPSConfigItem_GetDisplayName(WUPSConfigItemHandle handle, char *out_buf, int32_t out_len);
|
||||||
|
|
||||||
|
int32_t WUPSConfigItem_SetConfigID(WUPSConfigItemHandle handle, const char *configID);
|
||||||
|
|
||||||
|
int32_t WUPSConfigItem_GetConfigID(WUPSConfigItemHandle handle, char *out_buf, int32_t out);
|
||||||
|
|
||||||
|
int32_t WUPSConfig_Create(WUPSConfigHandle *out, const char *name);
|
||||||
|
|
||||||
|
int32_t WUPSConfigCategory_Destroy(WUPSConfigCategoryHandle handle);
|
||||||
|
|
||||||
|
int32_t WUPSConfig_GetName(WUPSConfigHandle handle, char *out_buf, int32_t out_len);
|
||||||
|
|
||||||
|
int32_t WUPSConfig_AddCategoryByName(WUPSConfigHandle handle, const char *categoryName, WUPSConfigCategoryHandle *out);
|
||||||
|
|
||||||
|
int32_t WUPSConfig_AddCategory(WUPSConfigHandle handle, WUPSConfigCategoryHandle category);
|
||||||
|
|
||||||
|
/*
|
||||||
|
int32_t WUPSConfig_GetCategoryCount(WUPSConfigHandle handle, int32_t *category_count);
|
||||||
|
|
||||||
|
int32_t WUPSConfig_GetCategories(WUPSConfigHandle handle, WUPSConfigCategoryHandle *categories_out, int32_t categories_out_size);
|
||||||
|
*/
|
||||||
|
|
||||||
|
int32_t WUPSConfigCategory_Create(WUPSConfigCategoryHandle *out, const char *name);
|
||||||
|
|
||||||
|
int32_t WUPSConfigCategory_GetName(WUPSConfigCategoryHandle handle, char *out_buf, int32_t out_len);
|
||||||
|
|
||||||
|
int32_t WUPSConfigCategory_AddItem(WUPSConfigCategoryHandle handle, WUPSConfigItemHandle item_Handle);
|
||||||
|
|
||||||
|
#define WUPSConfig_AddCategoryByNameHandled(__config__, __categoryName__, __out__) \
|
||||||
|
do { \
|
||||||
|
if (WUPSConfig_AddCategoryByName(__config__, __categoryName__, __out__) < 0) { \
|
||||||
|
WUPSConfig_Destroy(__config__); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define WUPSConfig_CreateHandled(__config__, __configName__) \
|
||||||
|
do { \
|
||||||
|
if (WUPSConfig_Create(__config__, __configName__) < 0) { \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -14,6 +14,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@ -43,21 +44,18 @@ typedef enum wups_loader_hook_type_t {
|
|||||||
WUPS_LOADER_HOOK_INIT_WRAPPER, /* Calls __init */
|
WUPS_LOADER_HOOK_INIT_WRAPPER, /* Calls __init */
|
||||||
WUPS_LOADER_HOOK_FINI_WRAPPER, /* Calls __fini */
|
WUPS_LOADER_HOOK_FINI_WRAPPER, /* Calls __fini */
|
||||||
|
|
||||||
WUPS_LOADER_HOOK_GET_CONFIG_DEPRECATED, /* Deprecated implementation */
|
WUPS_LOADER_HOOK_GET_CONFIG,
|
||||||
WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED, /* Deprecated implementation */
|
WUPS_LOADER_HOOK_CONFIG_CLOSED,
|
||||||
|
|
||||||
WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED, /* Deprecated implementation */
|
WUPS_LOADER_HOOK_INIT_STORAGE, /* Only for internal usage */
|
||||||
|
|
||||||
WUPS_LOADER_HOOK_INIT_PLUGIN, /* Called when exiting the plugin loader */
|
WUPS_LOADER_HOOK_INIT_PLUGIN, /* Called when exiting the plugin loader */
|
||||||
WUPS_LOADER_HOOK_DEINIT_PLUGIN, /* Called when re-entering the plugin loader */
|
WUPS_LOADER_HOOK_DEINIT_PLUGIN, /* Called when re-entering the plugin loader */
|
||||||
WUPS_LOADER_HOOK_APPLICATION_STARTS, /* Called when an application gets started */
|
WUPS_LOADER_HOOK_APPLICATION_STARTS, /* Called when an application gets started */
|
||||||
WUPS_LOADER_HOOK_RELEASE_FOREGROUND, /* Called when a foreground is going to be released */
|
WUPS_LOADER_HOOK_RELEASE_FOREGROUND, /* Called when an foreground is going to be released */
|
||||||
WUPS_LOADER_HOOK_ACQUIRED_FOREGROUND, /* Called when a foreground is acquired */
|
WUPS_LOADER_HOOK_ACQUIRED_FOREGROUND, /* Called when an foreground is acquired */
|
||||||
WUPS_LOADER_HOOK_APPLICATION_REQUESTS_EXIT, /* Called when an application wants to exit */
|
WUPS_LOADER_HOOK_APPLICATION_REQUESTS_EXIT, /* Called when an application wants to exit */
|
||||||
WUPS_LOADER_HOOK_APPLICATION_ENDS, /* Called when an application ends */
|
WUPS_LOADER_HOOK_APPLICATION_ENDS, /* Called when an application ends */
|
||||||
|
|
||||||
WUPS_LOADER_HOOK_INIT_STORAGE, /* Only for internal usage */
|
|
||||||
WUPS_LOADER_HOOK_INIT_CONFIG, /* Only for internal usage */
|
|
||||||
} wups_loader_hook_type_t;
|
} wups_loader_hook_type_t;
|
||||||
|
|
||||||
typedef struct wups_loader_hook_t {
|
typedef struct wups_loader_hook_t {
|
||||||
@ -100,35 +98,30 @@ typedef struct wups_loader_hook_t {
|
|||||||
WUPS_HOOK_EX(WUPS_LOADER_HOOK_APPLICATION_ENDS, on_app_ending); \
|
WUPS_HOOK_EX(WUPS_LOADER_HOOK_APPLICATION_ENDS, on_app_ending); \
|
||||||
void on_app_ending(void)
|
void on_app_ending(void)
|
||||||
|
|
||||||
#define WUPS_GET_CONFIG(x) \
|
#define WUPS_GET_CONFIG() \
|
||||||
static_assert(false, "Please use \"WUPSConfigAPI_Init\" inside \"INITIALIZE_PLUGIN\" to provide a callback instead");
|
WUPSConfigHandle on_get_wups_config(void); \
|
||||||
|
WUPS_HOOK_EX(WUPS_LOADER_HOOK_GET_CONFIG, on_get_wups_config); \
|
||||||
|
WUPSConfigHandle on_get_wups_config(void)
|
||||||
|
|
||||||
#define WUPS_CONFIG_CLOSED() \
|
#define WUPS_CONFIG_CLOSED() \
|
||||||
static_assert(false, "Please use \"WUPSConfigAPI_Init\" inside \"INITIALIZE_PLUGIN\" to provide a callback instead");
|
void on_wups_config_closed(void); \
|
||||||
|
WUPS_HOOK_EX(WUPS_LOADER_HOOK_CONFIG_CLOSED, on_wups_config_closed); \
|
||||||
|
void on_wups_config_closed(void)
|
||||||
|
|
||||||
#define WUPS_USE_STORAGE(x) \
|
#define WUPS_USE_STORAGE(x) \
|
||||||
WUPS_META(storage_id, x); \
|
WUPS_META(storage_id, x); \
|
||||||
void init_storage(wups_loader_init_storage_args_t); \
|
void init_storage(wups_loader_init_storage_args_t); \
|
||||||
WUPS_HOOK_EX(WUPS_LOADER_HOOK_INIT_STORAGE, init_storage); \
|
WUPS_HOOK_EX(WUPS_LOADER_HOOK_INIT_STORAGE, init_storage); \
|
||||||
void init_storage(wups_loader_init_storage_args_t args) { \
|
void init_storage(wups_loader_init_storage_args_t args) { \
|
||||||
WUPSStorageAPI_InitInternal(args); \
|
WUPS_InitStorage(args); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#define __EXTERN_C_MACRO extern "C"
|
#define __EXTERN_C_MACRO extern "C"
|
||||||
#else
|
#else
|
||||||
#define __EXTERN_C_MACRO
|
#define __EXTERN_C_MACRO
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define WUPS_INIT_CONFIG_FUNCTIONS() \
|
|
||||||
__EXTERN_C_MACRO WUPSConfigAPIStatus WUPSConfigAPI_InitLibrary_Internal(wups_loader_init_config_args_t args); \
|
|
||||||
void wups_init_config_functions(wups_loader_init_config_args_t); \
|
|
||||||
WUPS_HOOK_EX(WUPS_LOADER_HOOK_INIT_CONFIG, wups_init_config_functions); \
|
|
||||||
void wups_init_config_functions(wups_loader_init_config_args_t args) { \
|
|
||||||
WUPSConfigAPI_InitLibrary_Internal(args); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define WUPS_USE_WUT_MALLOC() \
|
#define WUPS_USE_WUT_MALLOC() \
|
||||||
__EXTERN_C_MACRO void __init_wut_malloc(); \
|
__EXTERN_C_MACRO void __init_wut_malloc(); \
|
||||||
void on_init_wut_malloc() { \
|
void on_init_wut_malloc() { \
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define WUPS_VERSION_STR "0.8.0"
|
#define WUPS_VERSION_STR "0.7.1"
|
||||||
#define WUPS_PLUGIN_NAME(__plugin_name) \
|
#define WUPS_PLUGIN_NAME(__plugin_name) \
|
||||||
WUPS_META(name, __plugin_name); \
|
WUPS_META(name, __plugin_name); \
|
||||||
WUPS_META(wups, WUPS_VERSION_STR); \
|
WUPS_META(wups, WUPS_VERSION_STR); \
|
||||||
@ -48,10 +48,14 @@ extern "C" {
|
|||||||
WUPS_USE_WUT_STDCPP(); \
|
WUPS_USE_WUT_STDCPP(); \
|
||||||
WUPS___INIT_WRAPPER(); \
|
WUPS___INIT_WRAPPER(); \
|
||||||
WUPS___FINI_WRAPPER(); \
|
WUPS___FINI_WRAPPER(); \
|
||||||
WUPS_INIT_CONFIG_FUNCTIONS(); \
|
__EXTERN_C_MACRO void abort(); \
|
||||||
|
__EXTERN_C_MACRO void OSFatal(const char *msg); \
|
||||||
|
void abort() { \
|
||||||
|
OSFatal(__plugin_name ": abort() called. Uncaught exception?"); \
|
||||||
|
while (1) \
|
||||||
|
; \
|
||||||
|
} \
|
||||||
WUPS_META(buildtimestamp, __DATE__ " " __TIME__); \
|
WUPS_META(buildtimestamp, __DATE__ " " __TIME__); \
|
||||||
extern const char wups_meta_plugin_name[] WUPS_SECTION("meta"); \
|
|
||||||
const char wups_meta_plugin_name[] = __plugin_name; \
|
|
||||||
extern const char wups_meta_info_dump[] WUPS_SECTION("meta"); \
|
extern const char wups_meta_info_dump[] WUPS_SECTION("meta"); \
|
||||||
const char wups_meta_info_dump[] = "(plugin: " __plugin_name ";" \
|
const char wups_meta_info_dump[] = "(plugin: " __plugin_name ";" \
|
||||||
"wups " WUPS_VERSION_STR ";" \
|
"wups " WUPS_VERSION_STR ";" \
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
#include <coreinit/debug.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
#define WUPS_DEBUG_REPORT(fmt, ...) OSReport(fmt, ##__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define WUPS_DEBUG_REPORT(fmt, ...)
|
|
||||||
#endif
|
|
@ -1,67 +0,0 @@
|
|||||||
#include <wups/config/WUPSConfigCategory.h>
|
|
||||||
#include <wups/config_api.h>
|
|
||||||
|
|
||||||
WUPSConfigCategory::WUPSConfigCategory(WUPSConfigCategoryHandle handle) noexcept : mHandle(handle) {
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigCategory::~WUPSConfigCategory() {
|
|
||||||
if (mHandle.handle != nullptr) {
|
|
||||||
WUPSConfigAPI_Category_Destroy(mHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<WUPSConfigCategory> WUPSConfigCategory::Create(std::string_view name, WUPSConfigAPIStatus &error) noexcept {
|
|
||||||
WUPSConfigCategoryHandle catHandle;
|
|
||||||
if ((error = WUPSConfigAPI_Category_Create({.name = name.data()}, &catHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return WUPSConfigCategory(catHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigCategory WUPSConfigCategory::Create(std::string_view name) {
|
|
||||||
WUPSConfigAPIStatus error;
|
|
||||||
auto res = Create(name, error);
|
|
||||||
if (!res) {
|
|
||||||
throw std::runtime_error{std::string("Failed to create category: ").append(name)};
|
|
||||||
}
|
|
||||||
return std::move(*res);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WUPSConfigCategory::add(WUPSConfigCategory &&cat, WUPSConfigAPIStatus &error) noexcept {
|
|
||||||
if (mHandle.handle == nullptr || cat.getHandle().handle == nullptr) {
|
|
||||||
OSReport("mHandle %08X item %08X\n", mHandle.handle, cat.getHandle().handle);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((error = WUPSConfigAPI_Category_AddCategory(mHandle, cat.getHandle())) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
cat.release();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WUPSConfigCategory::add(WUPSConfigCategory &&cat) {
|
|
||||||
WUPSConfigAPIStatus err;
|
|
||||||
if (!add(std::move(cat), err)) {
|
|
||||||
throw std::runtime_error{"Failed to add category to category"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WUPSConfigCategory::add(WUPSConfigItem &&item, WUPSConfigAPIStatus &error) noexcept {
|
|
||||||
if (mHandle.handle == nullptr || item.getHandle().handle == nullptr) {
|
|
||||||
OSReport("mHandle %08X item %08X\n", mHandle.handle, item.getHandle().handle);
|
|
||||||
error = WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((error = WUPSConfigAPI_Category_AddItem(mHandle, item.getHandle())) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
item.release();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WUPSConfigCategory::add(WUPSConfigItem &&item) {
|
|
||||||
WUPSConfigAPIStatus err;
|
|
||||||
if (!add(std::move(item), err)) {
|
|
||||||
throw std::runtime_error{"Failed to add item to category"};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#include <wups/config/WUPSConfigItem.h>
|
|
||||||
#include <wups/config_api.h>
|
|
||||||
|
|
||||||
WUPSConfigItem::~WUPSConfigItem() {
|
|
||||||
if (mHandle.handle != nullptr) {
|
|
||||||
WUPSConfigAPI_Item_Destroy(mHandle);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +1,46 @@
|
|||||||
#include "wups/config/WUPSConfigItemBoolean.h"
|
#include "wups/config/WUPSConfigItemBoolean.h"
|
||||||
#include "wups/config_api.h"
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <wups/config.h>
|
#include <wups.h>
|
||||||
|
|
||||||
static void WUPSConfigItemBoolean_onCloseCallback(void *context) {
|
void WUPSConfigItemBoolean_onDelete(void *context);
|
||||||
auto *item = (ConfigItemBoolean *) context;
|
|
||||||
if (item->valueAtCreation != item->value && item->valueChangedCallback != nullptr) {
|
|
||||||
((BooleanValueChangedCallback) (item->valueChangedCallback))(item, item->value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void toggleValue(ConfigItemBoolean *item) {
|
int32_t WUPSConfigItemBoolean_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) {
|
||||||
item->value = !item->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WUPSConfigItemBoolean_onInput(void *context, WUPSConfigSimplePadData input) {
|
|
||||||
auto *item = (ConfigItemBoolean *) context;
|
|
||||||
if ((input.buttons_d & WUPS_CONFIG_BUTTON_A) == WUPS_CONFIG_BUTTON_A) {
|
|
||||||
toggleValue(item);
|
|
||||||
} else if (input.buttons_d & WUPS_CONFIG_BUTTON_LEFT && !item->value) {
|
|
||||||
toggleValue(item);
|
|
||||||
} else if ((input.buttons_d & WUPS_CONFIG_BUTTON_RIGHT) && item->value) {
|
|
||||||
toggleValue(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static int32_t WUPSConfigItemBoolean_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) {
|
|
||||||
auto *item = (ConfigItemBoolean *) context;
|
auto *item = (ConfigItemBoolean *) context;
|
||||||
snprintf(out_buf, out_size, " %s", item->value ? item->trueValue : item->falseValue);
|
snprintf(out_buf, out_size, " %s", item->value ? item->trueValue : item->falseValue);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t WUPSConfigItemBoolean_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) {
|
void toggleValue(ConfigItemBoolean *item) {
|
||||||
|
item->value = !item->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WUPSConfigItemBoolean_callCallback(void *context) {
|
||||||
|
auto *item = (ConfigItemBoolean *) context;
|
||||||
|
if (item->callback != nullptr) {
|
||||||
|
((BooleanValueChangedCallback) (item->callback))(item, item->value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WUPSConfigItemBoolean_onButtonPressed(void *context, WUPSConfigButtons buttons) {
|
||||||
|
auto *item = (ConfigItemBoolean *) context;
|
||||||
|
if ((buttons & WUPS_CONFIG_BUTTON_A) == WUPS_CONFIG_BUTTON_A) {
|
||||||
|
toggleValue(item);
|
||||||
|
} else if (buttons & WUPS_CONFIG_BUTTON_LEFT && !item->value) {
|
||||||
|
toggleValue(item);
|
||||||
|
} else if ((buttons & WUPS_CONFIG_BUTTON_RIGHT) && item->value) {
|
||||||
|
toggleValue(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WUPSConfigItemBoolean_isMovementAllowed(void *context) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t WUPSConfigItemBoolean_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) {
|
||||||
auto *item = (ConfigItemBoolean *) context;
|
auto *item = (ConfigItemBoolean *) context;
|
||||||
if (item->value) {
|
if (item->value) {
|
||||||
snprintf(out_buf, out_size, " %s >", item->trueValue);
|
snprintf(out_buf, out_size, " %s >", item->trueValue);
|
||||||
@ -42,114 +50,65 @@ static int32_t WUPSConfigItemBoolean_getCurrentValueSelectedDisplay(void *contex
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WUPSConfigItemBoolean_restoreDefault(void *context) {
|
void WUPSConfigItemBoolean_restoreDefault(void *context) {
|
||||||
auto *item = (ConfigItemBoolean *) context;
|
auto *item = (ConfigItemBoolean *) context;
|
||||||
item->value = item->defaultValue;
|
item->value = item->defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WUPSConfigItemBoolean_Cleanup(ConfigItemBoolean *item) {
|
void WUPSConfigItemBoolean_onSelected(void *context, bool isSelected) {
|
||||||
if (!item) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
free((void *) item->identifier);
|
|
||||||
free(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WUPSConfigItemBoolean_onDelete(void *context) {
|
extern "C" bool
|
||||||
WUPSConfigItemBoolean_Cleanup((ConfigItemBoolean *) context);
|
WUPSConfigItemBoolean_AddToCategoryEx(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, bool defaultValue, BooleanValueChangedCallback callback, const char *trueValue,
|
||||||
}
|
const char *falseValue) {
|
||||||
|
if (cat == 0) {
|
||||||
extern "C" WUPSConfigAPIStatus
|
return false;
|
||||||
WUPSConfigItemBoolean_CreateEx(const char *identifier,
|
|
||||||
const char *displayName,
|
|
||||||
bool defaultValue,
|
|
||||||
bool currentValue,
|
|
||||||
BooleanValueChangedCallback callback,
|
|
||||||
const char *trueValue,
|
|
||||||
const char *falseValue,
|
|
||||||
WUPSConfigItemHandle *outHandle) {
|
|
||||||
if (outHandle == nullptr) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
}
|
||||||
auto *item = (ConfigItemBoolean *) malloc(sizeof(ConfigItemBoolean));
|
auto *item = (ConfigItemBoolean *) malloc(sizeof(ConfigItemBoolean));
|
||||||
if (item == nullptr) {
|
if (item == nullptr) {
|
||||||
return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identifier != nullptr) {
|
if (configId != nullptr) {
|
||||||
item->identifier = strdup(identifier);
|
item->configId = strdup(configId);
|
||||||
} else {
|
} else {
|
||||||
item->identifier = nullptr;
|
item->configId = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
item->defaultValue = defaultValue;
|
item->defaultValue = defaultValue;
|
||||||
item->value = currentValue;
|
item->value = defaultValue;
|
||||||
item->valueAtCreation = currentValue;
|
item->callback = (void *) callback;
|
||||||
item->valueChangedCallback = (void *) callback;
|
|
||||||
snprintf(item->trueValue, sizeof(item->trueValue), "%s", trueValue);
|
snprintf(item->trueValue, sizeof(item->trueValue), "%s", trueValue);
|
||||||
snprintf(item->falseValue, sizeof(item->falseValue), "%s", falseValue);
|
snprintf(item->falseValue, sizeof(item->falseValue), "%s", falseValue);
|
||||||
|
|
||||||
WUPSConfigAPIItemCallbacksV2 callbacks = {
|
WUPSConfigCallbacks_t callbacks = {
|
||||||
.getCurrentValueDisplay = &WUPSConfigItemBoolean_getCurrentValueDisplay,
|
.getCurrentValueDisplay = &WUPSConfigItemBoolean_getCurrentValueDisplay,
|
||||||
.getCurrentValueSelectedDisplay = &WUPSConfigItemBoolean_getCurrentValueSelectedDisplay,
|
.getCurrentValueSelectedDisplay = &WUPSConfigItemBoolean_getCurrentValueSelectedDisplay,
|
||||||
.onSelected = nullptr,
|
.onSelected = &WUPSConfigItemBoolean_onSelected,
|
||||||
.restoreDefault = &WUPSConfigItemBoolean_restoreDefault,
|
.restoreDefault = &WUPSConfigItemBoolean_restoreDefault,
|
||||||
.isMovementAllowed = nullptr,
|
.isMovementAllowed = &WUPSConfigItemBoolean_isMovementAllowed,
|
||||||
.onCloseCallback = &WUPSConfigItemBoolean_onCloseCallback,
|
.callCallback = &WUPSConfigItemBoolean_callCallback,
|
||||||
.onInput = &WUPSConfigItemBoolean_onInput,
|
.onButtonPressed = &WUPSConfigItemBoolean_onButtonPressed,
|
||||||
.onInputEx = nullptr,
|
|
||||||
.onDelete = &WUPSConfigItemBoolean_onDelete};
|
.onDelete = &WUPSConfigItemBoolean_onDelete};
|
||||||
|
|
||||||
WUPSConfigAPIItemOptionsV2 options = {
|
if (WUPSConfigItem_Create(&item->handle, configId, displayName, callbacks, item) < 0) {
|
||||||
.displayName = displayName,
|
free(item);
|
||||||
.context = item,
|
return false;
|
||||||
.callbacks = callbacks,
|
|
||||||
};
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus err;
|
|
||||||
if ((err = WUPSConfigAPI_Item_Create(options, &item->handle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
WUPSConfigItemBoolean_Cleanup(item);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*outHandle = item->handle;
|
if (WUPSConfigCategory_AddItem(cat, item->handle) < 0) {
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
WUPSConfigItem_Destroy(item->handle);
|
||||||
}
|
return false;
|
||||||
|
|
||||||
extern "C" WUPSConfigAPIStatus
|
|
||||||
WUPSConfigItemBoolean_AddToCategoryEx(WUPSConfigCategoryHandle cat,
|
|
||||||
const char *identifier,
|
|
||||||
const char *displayName,
|
|
||||||
bool defaultValue,
|
|
||||||
bool currentValue,
|
|
||||||
BooleanValueChangedCallback callback,
|
|
||||||
const char *trueValue,
|
|
||||||
const char *falseValue) {
|
|
||||||
WUPSConfigItemHandle itemHandle;
|
|
||||||
WUPSConfigAPIStatus res;
|
|
||||||
if ((res = WUPSConfigItemBoolean_CreateEx(identifier,
|
|
||||||
displayName,
|
|
||||||
defaultValue, currentValue,
|
|
||||||
callback,
|
|
||||||
trueValue, falseValue,
|
|
||||||
&itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
if ((res = WUPSConfigAPI_Category_AddItem(cat, itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
|
|
||||||
WUPSConfigAPI_Item_Destroy(itemHandle);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" WUPSConfigAPIStatus
|
void WUPSConfigItemBoolean_onDelete(void *context) {
|
||||||
WUPSConfigItemBoolean_AddToCategory(WUPSConfigCategoryHandle cat,
|
auto *item = (ConfigItemBoolean *) context;
|
||||||
const char *identifier,
|
free(item->configId);
|
||||||
const char *displayName,
|
free(item);
|
||||||
bool defaultValue,
|
}
|
||||||
bool currentValue,
|
|
||||||
BooleanValueChangedCallback callback) {
|
extern "C" bool WUPSConfigItemBoolean_AddToCategory(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName, bool defaultValue, BooleanValueChangedCallback callback) {
|
||||||
return WUPSConfigItemBoolean_AddToCategoryEx(cat, identifier, displayName, defaultValue, currentValue, callback, "true", "false");
|
return WUPSConfigItemBoolean_AddToCategoryEx(cat, configID, displayName, defaultValue, callback, "true", "false");
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
#include "wups/config/WUPSConfigItemBoolean.h"
|
|
||||||
|
|
||||||
std::optional<WUPSConfigItemBoolean> WUPSConfigItemBoolean::CreateEx(std::optional<std::string> identifier, std::string_view displayName, bool defaultValue, bool currentValue, BooleanValueChangedCallback callback, std::string_view trueValue, std::string_view falseValue, WUPSConfigAPIStatus &err) noexcept {
|
|
||||||
WUPSConfigItemHandle itemHandle;
|
|
||||||
if ((err = WUPSConfigItemBoolean_CreateEx(identifier ? identifier->data() : nullptr,
|
|
||||||
displayName.data(),
|
|
||||||
defaultValue, currentValue,
|
|
||||||
callback,
|
|
||||||
trueValue.data(),
|
|
||||||
falseValue.data(),
|
|
||||||
&itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return WUPSConfigItemBoolean(itemHandle);
|
|
||||||
}
|
|
||||||
WUPSConfigItemBoolean WUPSConfigItemBoolean::CreateEx(std::optional<std::string> identifier, std::string_view displayName, bool defaultValue, bool currentValue, BooleanValueChangedCallback callback, std::string_view trueValue, std::string_view falseValue) {
|
|
||||||
WUPSConfigAPIStatus err;
|
|
||||||
auto result = CreateEx(std::move(identifier), displayName, defaultValue, currentValue, callback, trueValue, falseValue, err);
|
|
||||||
if (!result) {
|
|
||||||
throw std::runtime_error(std::string("Failed to create WUPSConfigItemBoolean: ").append(WUPSConfigAPI_GetStatusStr(err)));
|
|
||||||
}
|
|
||||||
return std::move(*result);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<WUPSConfigItemBoolean> WUPSConfigItemBoolean::Create(std::optional<std::string> identifier, std::string_view displayName, bool defaultValue, bool currentValue, BooleanValueChangedCallback callback, WUPSConfigAPIStatus &err) noexcept {
|
|
||||||
return CreateEx(std::move(identifier), displayName, defaultValue, currentValue, callback, "true", "false", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigItemBoolean WUPSConfigItemBoolean::Create(std::optional<std::string> identifier, std::string_view displayName, bool defaultValue, bool currentValue, BooleanValueChangedCallback callback) {
|
|
||||||
WUPSConfigAPIStatus err = WUPSCONFIG_API_RESULT_UNKNOWN_ERROR;
|
|
||||||
auto res = Create(std::move(identifier), displayName, defaultValue, currentValue, callback, err);
|
|
||||||
if (!res) {
|
|
||||||
throw std::runtime_error(std::string("Failed to create WUPSConfigItemBoolean: ").append(WUPSConfigAPI_GetStatusStr(err)));
|
|
||||||
}
|
|
||||||
return std::move(*res);
|
|
||||||
}
|
|
@ -1,27 +1,33 @@
|
|||||||
#include "wups/config/WUPSConfigItemIntegerRange.h"
|
#include "wups/config/WUPSConfigItemIntegerRange.h"
|
||||||
#include "wups/config_api.h"
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <wups/config.h>
|
#include <wups.h>
|
||||||
|
|
||||||
void WUPSConfigItemIntegerRange_onCloseCallback(void *context) {
|
int32_t WUPSConfigItemIntegerRange_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) {
|
||||||
auto *item = (ConfigItemIntegerRange *) context;
|
auto *item = (ConfigItemIntegerRange *) context;
|
||||||
if (item->valueAtCreation != item->value && item->valueChangedCallback != nullptr) {
|
snprintf(out_buf, out_size, "%d", item->value);
|
||||||
((IntegerRangeValueChangedCallback) item->valueChangedCallback)(item, item->value);
|
return 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WUPSConfigItemIntegerRange_onInput(void *context, WUPSConfigSimplePadData input) {
|
bool WUPSConfigItemIntegerRange_callCallback(void *context) {
|
||||||
auto *item = (ConfigItemIntegerRange *) context;
|
auto *item = (ConfigItemIntegerRange *) context;
|
||||||
|
if (item->callback != nullptr) {
|
||||||
|
((IntegerRangeValueChangedCallback) item->callback)(item, item->value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (input.buttons_d & WUPS_CONFIG_BUTTON_LEFT) {
|
void WUPSConfigItemIntegerRange_onButtonPressed(void *context, WUPSConfigButtons buttons) {
|
||||||
|
auto *item = (ConfigItemIntegerRange *) context;
|
||||||
|
if (buttons & WUPS_CONFIG_BUTTON_LEFT) {
|
||||||
item->value--;
|
item->value--;
|
||||||
} else if ((input.buttons_d & WUPS_CONFIG_BUTTON_RIGHT)) {
|
} else if ((buttons & WUPS_CONFIG_BUTTON_RIGHT)) {
|
||||||
item->value++;
|
item->value++;
|
||||||
} else if ((input.buttons_d & WUPS_CONFIG_BUTTON_L)) {
|
} else if ((buttons & WUPS_CONFIG_BUTTON_L)) {
|
||||||
item->value = item->value - 50;
|
item->value = item->value - 50;
|
||||||
} else if ((input.buttons_d & WUPS_CONFIG_BUTTON_R)) {
|
} else if ((buttons & WUPS_CONFIG_BUTTON_R)) {
|
||||||
item->value = item->value + 50;
|
item->value = item->value + 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,10 +38,8 @@ void WUPSConfigItemIntegerRange_onInput(void *context, WUPSConfigSimplePadData i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t WUPSConfigItemIntegerRange_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) {
|
bool WUPSConfigItemIntegerRange_isMovementAllowed(void *context) {
|
||||||
auto *item = (ConfigItemIntegerRange *) context;
|
return true;
|
||||||
snprintf(out_buf, out_size, " %d", item->value);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t WUPSConfigItemIntegerRange_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) {
|
int32_t WUPSConfigItemIntegerRange_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) {
|
||||||
@ -55,99 +59,55 @@ void WUPSConfigItemIntegerRange_restoreDefault(void *context) {
|
|||||||
item->value = item->defaultValue;
|
item->value = item->defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WUPSConfigItemIntegerRange_Cleanup(ConfigItemIntegerRange *item) {
|
void WUPSConfigItemIntegerRange_onDelete(void *context) {
|
||||||
if (!item) {
|
auto *item = (ConfigItemIntegerRange *) context;
|
||||||
return;
|
free(item->configId);
|
||||||
}
|
|
||||||
free((void *) item->identifier);
|
|
||||||
free(item);
|
free(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WUPSConfigItemIntegerRange_onDelete(void *context) {
|
void WUPSConfigItemIntegerRange_onSelected(void *context, bool isSelected) {
|
||||||
WUPSConfigItemIntegerRange_Cleanup((ConfigItemIntegerRange *) context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" WUPSConfigAPIStatus
|
extern "C" bool WUPSConfigItemIntegerRange_AddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, int32_t defaultValue, int32_t minValue, int32_t maxValue,
|
||||||
WUPSConfigItemIntegerRange_Create(const char *identifier,
|
IntegerRangeValueChangedCallback callback) {
|
||||||
const char *displayName,
|
if (cat == 0) {
|
||||||
int32_t defaultValue, int32_t currentValue,
|
return false;
|
||||||
int32_t minValue, int32_t maxValue,
|
|
||||||
IntegerRangeValueChangedCallback callback,
|
|
||||||
WUPSConfigItemHandle *outHandle) {
|
|
||||||
if (outHandle == nullptr) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
}
|
||||||
if (maxValue < minValue || defaultValue < minValue || defaultValue > maxValue || currentValue < minValue || currentValue > maxValue) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
*outHandle = {};
|
|
||||||
auto *item = (ConfigItemIntegerRange *) malloc(sizeof(ConfigItemIntegerRange));
|
auto *item = (ConfigItemIntegerRange *) malloc(sizeof(ConfigItemIntegerRange));
|
||||||
if (item == nullptr) {
|
if (item == nullptr) {
|
||||||
return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identifier != nullptr) {
|
if (configId != nullptr) {
|
||||||
item->identifier = strdup(identifier);
|
item->configId = strdup(configId);
|
||||||
} else {
|
} else {
|
||||||
item->identifier = nullptr;
|
item->configId = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
item->defaultValue = defaultValue;
|
item->defaultValue = defaultValue;
|
||||||
item->value = currentValue;
|
item->value = defaultValue;
|
||||||
item->valueAtCreation = currentValue;
|
item->minValue = minValue;
|
||||||
item->minValue = minValue;
|
item->maxValue = maxValue;
|
||||||
item->maxValue = maxValue;
|
item->callback = (void *) callback;
|
||||||
item->valueChangedCallback = (void *) callback;
|
|
||||||
|
|
||||||
WUPSConfigAPIItemCallbacksV2 callbacks = {
|
WUPSConfigCallbacks_t callbacks = {
|
||||||
.getCurrentValueDisplay = &WUPSConfigItemIntegerRange_getCurrentValueDisplay,
|
.getCurrentValueDisplay = &WUPSConfigItemIntegerRange_getCurrentValueDisplay,
|
||||||
.getCurrentValueSelectedDisplay = &WUPSConfigItemIntegerRange_getCurrentValueSelectedDisplay,
|
.getCurrentValueSelectedDisplay = &WUPSConfigItemIntegerRange_getCurrentValueSelectedDisplay,
|
||||||
.onSelected = nullptr,
|
.onSelected = &WUPSConfigItemIntegerRange_onSelected,
|
||||||
.restoreDefault = &WUPSConfigItemIntegerRange_restoreDefault,
|
.restoreDefault = &WUPSConfigItemIntegerRange_restoreDefault,
|
||||||
.isMovementAllowed = nullptr,
|
.isMovementAllowed = &WUPSConfigItemIntegerRange_isMovementAllowed,
|
||||||
.onCloseCallback = &WUPSConfigItemIntegerRange_onCloseCallback,
|
.callCallback = &WUPSConfigItemIntegerRange_callCallback,
|
||||||
.onInput = &WUPSConfigItemIntegerRange_onInput,
|
.onButtonPressed = &WUPSConfigItemIntegerRange_onButtonPressed,
|
||||||
.onInputEx = nullptr,
|
.onDelete = &WUPSConfigItemIntegerRange_onDelete};
|
||||||
.onDelete = &WUPSConfigItemIntegerRange_onDelete,
|
|
||||||
|
if (WUPSConfigItem_Create(&(item->handle), configId, displayName, callbacks, item) < 0) {
|
||||||
|
free(item);
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
WUPSConfigAPIItemOptionsV2 options = {
|
if (WUPSConfigCategory_AddItem(cat, item->handle) < 0) {
|
||||||
.displayName = displayName,
|
WUPSConfigItem_Destroy(item->handle);
|
||||||
.context = item,
|
return false;
|
||||||
.callbacks = callbacks,
|
|
||||||
};
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus err;
|
|
||||||
if ((err = WUPSConfigAPI_Item_Create(options, &(item->handle))) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
WUPSConfigItemIntegerRange_Cleanup(item);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
*outHandle = item->handle;
|
return true;
|
||||||
|
}
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" WUPSConfigAPIStatus
|
|
||||||
WUPSConfigItemIntegerRange_AddToCategory(WUPSConfigCategoryHandle cat,
|
|
||||||
const char *identifier,
|
|
||||||
const char *displayName,
|
|
||||||
int32_t defaultValue, int32_t currentValue,
|
|
||||||
int32_t minValue, int32_t maxValue,
|
|
||||||
IntegerRangeValueChangedCallback callback) {
|
|
||||||
WUPSConfigItemHandle itemHandle;
|
|
||||||
WUPSConfigAPIStatus res;
|
|
||||||
if ((res = WUPSConfigItemIntegerRange_Create(identifier,
|
|
||||||
displayName,
|
|
||||||
defaultValue, currentValue,
|
|
||||||
minValue, maxValue,
|
|
||||||
callback,
|
|
||||||
&itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((res = WUPSConfigAPI_Category_AddItem(cat, itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
WUPSConfigAPI_Item_Destroy(itemHandle);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
#include "wups/config/WUPSConfigItemIntegerRange.h"
|
|
||||||
|
|
||||||
std::optional<WUPSConfigItemIntegerRange> WUPSConfigItemIntegerRange::Create(
|
|
||||||
std::optional<std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
int32_t defaultValue, int32_t currentValue,
|
|
||||||
int32_t minValue, int32_t maxValue,
|
|
||||||
IntegerRangeValueChangedCallback valuesChangedCallback,
|
|
||||||
WUPSConfigAPIStatus &err) noexcept {
|
|
||||||
WUPSConfigItemHandle itemHandle;
|
|
||||||
if ((err = WUPSConfigItemIntegerRange_Create(identifier ? identifier->c_str() : nullptr,
|
|
||||||
displayName.data(),
|
|
||||||
defaultValue, currentValue,
|
|
||||||
minValue, maxValue,
|
|
||||||
valuesChangedCallback,
|
|
||||||
&itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return WUPSConfigItemIntegerRange(itemHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigItemIntegerRange WUPSConfigItemIntegerRange::Create(
|
|
||||||
std::optional<std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
int32_t defaultValue, int32_t currentValue,
|
|
||||||
int32_t minValue, int32_t maxValue,
|
|
||||||
IntegerRangeValueChangedCallback valuesChangedCallback) {
|
|
||||||
WUPSConfigAPIStatus err = WUPSCONFIG_API_RESULT_UNKNOWN_ERROR;
|
|
||||||
auto result = Create(std::move(identifier), displayName, defaultValue, currentValue, minValue, maxValue, valuesChangedCallback, err);
|
|
||||||
if (!result) {
|
|
||||||
throw std::runtime_error(std::string("Failed to create WUPSConfigItemIntegerRange: ").append(WUPSConfigAPI_GetStatusStr(err)));
|
|
||||||
}
|
|
||||||
return std::move(*result);
|
|
||||||
}
|
|
@ -1,11 +1,12 @@
|
|||||||
#include "wups/config/WUPSConfigItemMultipleValues.h"
|
#include "wups/config/WUPSConfigItemMultipleValues.h"
|
||||||
#include "wups/config_api.h"
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <wups/config.h>
|
#include <wups.h>
|
||||||
|
|
||||||
static int32_t WUPSConfigItemMultipleValues_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) {
|
void WUPSConfigItemMultipleValues_onDelete(void *context);
|
||||||
|
|
||||||
|
int32_t WUPSConfigItemMultipleValues_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) {
|
||||||
auto *item = (ConfigItemMultipleValues *) context;
|
auto *item = (ConfigItemMultipleValues *) context;
|
||||||
|
|
||||||
if (item->values && item->valueIndex >= 0 && item->valueIndex < item->valueCount) {
|
if (item->values && item->valueIndex >= 0 && item->valueIndex < item->valueCount) {
|
||||||
@ -18,20 +19,20 @@ static int32_t WUPSConfigItemMultipleValues_getCurrentValueDisplay(void *context
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WUPSConfigItemMultipleValues_onCloseCallback(void *context) {
|
bool WUPSConfigItemMultipleValues_callCallback(void *context) {
|
||||||
auto *item = (ConfigItemMultipleValues *) context;
|
auto *item = (ConfigItemMultipleValues *) context;
|
||||||
if (item->valueIndexAtCreation != item->valueIndex && item->valueChangedCallback != nullptr) {
|
if (item->callback != nullptr && item->values && item->valueIndex >= 0 && item->valueIndex < item->valueCount) {
|
||||||
if (item->values && item->valueIndex >= 0 && item->valueIndex < item->valueCount) {
|
((MultipleValuesChangedCallback) (item->callback))(item, item->values[item->valueIndex].value);
|
||||||
((MultipleValuesChangedCallback) (item->valueChangedCallback))(item, item->values[item->valueIndex].value);
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WUPSConfigItemMultipleValues_onInput(void *context, WUPSConfigSimplePadData input) {
|
void WUPSConfigItemMultipleValues_onButtonPressed(void *context, WUPSConfigButtons buttons) {
|
||||||
auto *item = (ConfigItemMultipleValues *) context;
|
auto *item = (ConfigItemMultipleValues *) context;
|
||||||
if (input.buttons_d & WUPS_CONFIG_BUTTON_LEFT) {
|
if (buttons & WUPS_CONFIG_BUTTON_LEFT) {
|
||||||
item->valueIndex--;
|
item->valueIndex--;
|
||||||
} else if (input.buttons_d & WUPS_CONFIG_BUTTON_RIGHT) {
|
} else if (buttons & WUPS_CONFIG_BUTTON_RIGHT) {
|
||||||
item->valueIndex++;
|
item->valueIndex++;
|
||||||
}
|
}
|
||||||
if (item->valueIndex < 0) {
|
if (item->valueIndex < 0) {
|
||||||
@ -41,7 +42,11 @@ static void WUPSConfigItemMultipleValues_onInput(void *context, WUPSConfigSimple
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t WUPSConfigItemMultipleValues_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) {
|
bool WUPSConfigItemMultipleValues_isMovementAllowed(void *context) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t WUPSConfigItemMultipleValues_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) {
|
||||||
auto *item = (ConfigItemMultipleValues *) context;
|
auto *item = (ConfigItemMultipleValues *) context;
|
||||||
if (item->values && item->valueIndex >= 0 && item->valueIndex < item->valueCount) {
|
if (item->values && item->valueIndex >= 0 && item->valueIndex < item->valueCount) {
|
||||||
if (item->valueCount == 1) {
|
if (item->valueCount == 1) {
|
||||||
@ -59,119 +64,82 @@ static int32_t WUPSConfigItemMultipleValues_getCurrentValueSelectedDisplay(void
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WUPSConfigItemMultipleValues_restoreDefault(void *context) {
|
void WUPSConfigItemMultipleValues_restoreDefault(void *context) {
|
||||||
auto *item = (ConfigItemMultipleValues *) context;
|
auto *item = (ConfigItemMultipleValues *) context;
|
||||||
item->valueIndex = item->defaultValueIndex;
|
item->valueIndex = item->defaultValueIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WUPSConfigItemMultipleValues_Cleanup(ConfigItemMultipleValues *item) {
|
void WUPSConfigItemMultipleValues_onSelected(void *context, bool isSelected) {
|
||||||
if (!item) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < item->valueCount; ++i) {
|
|
||||||
free((void *) item->values[i].valueName);
|
|
||||||
}
|
|
||||||
|
|
||||||
free((void *) item->identifier);
|
|
||||||
|
|
||||||
free(item->values);
|
|
||||||
free(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WUPSConfigItemMultipleValues_onDelete(void *context) {
|
extern "C" bool
|
||||||
auto *item = (ConfigItemMultipleValues *) context;
|
WUPSConfigItemMultipleValues_AddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName,
|
||||||
WUPSConfigItemMultipleValues_Cleanup(item);
|
int32_t defaultValueIndex, ConfigItemMultipleValuesPair *possibleValues,
|
||||||
}
|
int pairCount, MultipleValuesChangedCallback callback) {
|
||||||
|
if (cat == 0 || displayName == nullptr || possibleValues == nullptr || pairCount < 0) {
|
||||||
extern "C" WUPSConfigAPIStatus
|
return false;
|
||||||
WUPSConfigItemMultipleValues_Create(const char *identifier, const char *displayName,
|
|
||||||
int32_t defaultValueIndex, int currentValueIndex,
|
|
||||||
ConfigItemMultipleValuesPair *possibleValues,
|
|
||||||
int pairCount, MultipleValuesChangedCallback callback,
|
|
||||||
WUPSConfigItemHandle *outHandle) {
|
|
||||||
if (outHandle == nullptr || displayName == nullptr || possibleValues == nullptr || pairCount < 0 ||
|
|
||||||
defaultValueIndex >= pairCount || currentValueIndex >= pairCount) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
}
|
||||||
*outHandle = {};
|
|
||||||
auto *item = (ConfigItemMultipleValues *) malloc(sizeof(ConfigItemMultipleValues));
|
auto *item = (ConfigItemMultipleValues *) malloc(sizeof(ConfigItemMultipleValues));
|
||||||
if (item == nullptr) {
|
if (item == nullptr) {
|
||||||
return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY;
|
return false;
|
||||||
}
|
|
||||||
*item = {};
|
|
||||||
|
|
||||||
item->values = (ConfigItemMultipleValuesPair *) malloc(sizeof(ConfigItemMultipleValuesPair) * pairCount);
|
|
||||||
if (!item->values) {
|
|
||||||
WUPSConfigItemMultipleValues_Cleanup(item);
|
|
||||||
return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identifier != nullptr) {
|
auto *values = (ConfigItemMultipleValuesPair *) malloc(sizeof(ConfigItemMultipleValuesPair) * pairCount);
|
||||||
item->identifier = strdup(identifier);
|
|
||||||
} else {
|
|
||||||
item->identifier = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < pairCount; ++i) {
|
for (int i = 0; i < pairCount; ++i) {
|
||||||
item->values[i].value = possibleValues[i].value;
|
values[i].value = possibleValues[i].value;
|
||||||
if (possibleValues[i].valueName == nullptr) {
|
if (possibleValues[i].valueName == nullptr) {
|
||||||
item->values[i].valueName = nullptr;
|
values[i].valueName = nullptr;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
item->values[i].valueName = strdup(possibleValues[i].valueName);
|
auto bufLen = strlen(possibleValues[i].valueName) + 1;
|
||||||
|
values[i].valueName = (char *) malloc(bufLen);
|
||||||
|
strncpy(values[i].valueName, possibleValues[i].valueName, bufLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
item->valueCount = pairCount;
|
item->valueCount = pairCount;
|
||||||
item->defaultValueIndex = defaultValueIndex;
|
item->values = values;
|
||||||
item->valueIndex = currentValueIndex;
|
item->valueIndex = defaultValueIndex;
|
||||||
item->valueIndexAtCreation = currentValueIndex;
|
item->defaultValueIndex = defaultValueIndex;
|
||||||
item->valueChangedCallback = (void *) callback;
|
item->callback = (void *) callback;
|
||||||
|
|
||||||
WUPSConfigAPIItemCallbacksV2 callbacks = {
|
if (configId != nullptr) {
|
||||||
|
item->configId = strdup(configId);
|
||||||
|
} else {
|
||||||
|
item->configId = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSConfigCallbacks_t callbacks = {
|
||||||
.getCurrentValueDisplay = &WUPSConfigItemMultipleValues_getCurrentValueDisplay,
|
.getCurrentValueDisplay = &WUPSConfigItemMultipleValues_getCurrentValueDisplay,
|
||||||
.getCurrentValueSelectedDisplay = &WUPSConfigItemMultipleValues_getCurrentValueSelectedDisplay,
|
.getCurrentValueSelectedDisplay = &WUPSConfigItemMultipleValues_getCurrentValueSelectedDisplay,
|
||||||
.onSelected = nullptr,
|
.onSelected = &WUPSConfigItemMultipleValues_onSelected,
|
||||||
.restoreDefault = &WUPSConfigItemMultipleValues_restoreDefault,
|
.restoreDefault = &WUPSConfigItemMultipleValues_restoreDefault,
|
||||||
.isMovementAllowed = nullptr,
|
.isMovementAllowed = &WUPSConfigItemMultipleValues_isMovementAllowed,
|
||||||
.onCloseCallback = &WUPSConfigItemMultipleValues_onCloseCallback,
|
.callCallback = &WUPSConfigItemMultipleValues_callCallback,
|
||||||
.onInput = &WUPSConfigItemMultipleValues_onInput,
|
.onButtonPressed = &WUPSConfigItemMultipleValues_onButtonPressed,
|
||||||
.onInputEx = nullptr,
|
|
||||||
.onDelete = &WUPSConfigItemMultipleValues_onDelete};
|
.onDelete = &WUPSConfigItemMultipleValues_onDelete};
|
||||||
|
|
||||||
WUPSConfigAPIItemOptionsV2 options = {
|
if (WUPSConfigItem_Create(&item->handle, configId, displayName, callbacks, item) < 0) {
|
||||||
.displayName = displayName,
|
free(item);
|
||||||
.context = item,
|
return false;
|
||||||
.callbacks = callbacks,
|
|
||||||
};
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus err;
|
|
||||||
if ((err = WUPSConfigAPI_Item_Create(options, &item->handle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
WUPSConfigItemMultipleValues_Cleanup(item);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
*outHandle = item->handle;
|
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
if (WUPSConfigCategory_AddItem(cat, item->handle) < 0) {
|
||||||
|
WUPSConfigItem_Destroy(item->handle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" WUPSConfigAPIStatus
|
void WUPSConfigItemMultipleValues_onDelete(void *context) {
|
||||||
WUPSConfigItemMultipleValues_AddToCategory(WUPSConfigCategoryHandle cat, const char *identifier, const char *displayName,
|
auto *item = (ConfigItemMultipleValues *) context;
|
||||||
int32_t defaultValueIndex, int currentValueIndex,
|
|
||||||
ConfigItemMultipleValuesPair *possibleValues, int pairCount,
|
for (int i = 0; i < item->valueCount; ++i) {
|
||||||
MultipleValuesChangedCallback callback) {
|
free(item->values[i].valueName);
|
||||||
WUPSConfigItemHandle itemHandle;
|
|
||||||
WUPSConfigAPIStatus res;
|
|
||||||
if ((res = WUPSConfigItemMultipleValues_Create(identifier,
|
|
||||||
displayName,
|
|
||||||
defaultValueIndex, currentValueIndex,
|
|
||||||
possibleValues, pairCount,
|
|
||||||
callback,
|
|
||||||
&itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
if ((res = WUPSConfigAPI_Category_AddItem(cat, itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
WUPSConfigAPI_Item_Destroy(itemHandle);
|
free(item->configId);
|
||||||
return res;
|
free(item->values);
|
||||||
}
|
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
free(item);
|
||||||
}
|
}
|
@ -1,102 +0,0 @@
|
|||||||
#include "wups/config/WUPSConfigItemMultipleValues.h"
|
|
||||||
|
|
||||||
|
|
||||||
std::optional<WUPSConfigItemMultipleValues> WUPSConfigItemMultipleValues::CreateFromIndex(
|
|
||||||
std::optional<const std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
int defaultValueIndex, int currentValueIndex,
|
|
||||||
const std::span<const ValuePair> &possibleValues,
|
|
||||||
MultipleValuesChangedCallback valuesChangedCallback,
|
|
||||||
WUPSConfigAPIStatus &err) noexcept {
|
|
||||||
auto *values = (ConfigItemMultipleValuesPair *) malloc(possibleValues.size_bytes());
|
|
||||||
if (!values) {
|
|
||||||
err = WUPSCONFIG_API_RESULT_OUT_OF_MEMORY;
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
int i = 0;
|
|
||||||
for (const auto &cur : possibleValues) {
|
|
||||||
values[i].value = cur.value;
|
|
||||||
values[i].valueName = cur.name.data();
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
WUPSConfigItemHandle itemHandle;
|
|
||||||
err = WUPSConfigItemMultipleValues_Create(
|
|
||||||
identifier ? identifier->c_str() : nullptr,
|
|
||||||
displayName.data(),
|
|
||||||
defaultValueIndex, currentValueIndex,
|
|
||||||
values, (int32_t) possibleValues.size(),
|
|
||||||
valuesChangedCallback,
|
|
||||||
&itemHandle);
|
|
||||||
free(values);
|
|
||||||
if (err != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return WUPSConfigItemMultipleValues(itemHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigItemMultipleValues WUPSConfigItemMultipleValues::CreateFromIndex(
|
|
||||||
std::optional<const std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
int defaultValueIndex, int currentValueIndex,
|
|
||||||
const std::span<const ValuePair> &possibleValues,
|
|
||||||
MultipleValuesChangedCallback valuesChangedCallback) {
|
|
||||||
WUPSConfigAPIStatus err = WUPSCONFIG_API_RESULT_UNKNOWN_ERROR;
|
|
||||||
auto result = CreateFromIndex(std::move(identifier), displayName, defaultValueIndex, currentValueIndex, possibleValues, valuesChangedCallback, err);
|
|
||||||
if (!result) {
|
|
||||||
throw std::runtime_error(std::string("Failed to create WUPSConfigItemMultipleValues: ").append(WUPSConfigAPI_GetStatusStr(err)));
|
|
||||||
}
|
|
||||||
return std::move(*result);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<WUPSConfigItemMultipleValues> WUPSConfigItemMultipleValues::CreateFromValue(
|
|
||||||
std::optional<const std::string> identifier,
|
|
||||||
std::string_view displayName,
|
|
||||||
uint32_t defaultValue, uint32_t currentValue,
|
|
||||||
const std::span<const ValuePair> &possibleValues,
|
|
||||||
MultipleValuesChangedCallback valuesChangedCallback,
|
|
||||||
WUPSConfigAPIStatus &err) noexcept {
|
|
||||||
int defaultIndex = -1;
|
|
||||||
int currentValueIndex = -1;
|
|
||||||
int i = 0;
|
|
||||||
for (const auto &cur : possibleValues) {
|
|
||||||
if (defaultIndex != -1 && currentValueIndex != -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (cur.value == currentValue) {
|
|
||||||
currentValueIndex = i;
|
|
||||||
}
|
|
||||||
if (cur.value == defaultValue) {
|
|
||||||
defaultIndex = i;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (defaultIndex == -1 || currentValueIndex == -1) {
|
|
||||||
err = WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WUPSConfigItemMultipleValues::CreateFromIndex(std::move(identifier),
|
|
||||||
displayName,
|
|
||||||
defaultIndex, currentValueIndex,
|
|
||||||
possibleValues,
|
|
||||||
valuesChangedCallback,
|
|
||||||
err);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigItemMultipleValues WUPSConfigItemMultipleValues::CreateFromValue(
|
|
||||||
std::optional<const std::string> identifier, std::string_view displayName,
|
|
||||||
int32_t defaultValue, int32_t currentValue,
|
|
||||||
const std::span<const ValuePair> &possibleValues,
|
|
||||||
MultipleValuesChangedCallback valuesChangedCallback) {
|
|
||||||
WUPSConfigAPIStatus err = WUPSCONFIG_API_RESULT_UNKNOWN_ERROR;
|
|
||||||
auto result = CreateFromValue(std::move(identifier),
|
|
||||||
displayName,
|
|
||||||
defaultValue, currentValue,
|
|
||||||
possibleValues,
|
|
||||||
valuesChangedCallback,
|
|
||||||
err);
|
|
||||||
if (!result) {
|
|
||||||
throw std::runtime_error(std::string("Failed to create WUPSConfigItemMultipleValues (\"").append(displayName).append("\":").append(WUPSConfigAPI_GetStatusStr(err)));
|
|
||||||
}
|
|
||||||
return std::move(*result);
|
|
||||||
}
|
|
@ -1,69 +1,76 @@
|
|||||||
#include "wups/config/WUPSConfigItemStub.h"
|
#include "wups/config/WUPSConfigItemStub.h"
|
||||||
#include "wups/config_api.h"
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <wups/config.h>
|
#include <wups.h>
|
||||||
|
|
||||||
static int32_t WUPSConfigItemStub_getEmptyTextValue(void *context, char *out_buf, int32_t out_size) {
|
void WUPSConfigItemStub_onDelete(void *context);
|
||||||
|
|
||||||
|
int32_t WUPSConfigItemStub_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) {
|
||||||
memset(out_buf, 0, out_size);
|
memset(out_buf, 0, out_size);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WUPSConfigItemStub_Cleanup(void *item) {
|
bool WUPSConfigItemStub_callCallback(void *context) {
|
||||||
free(item);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" WUPSConfigAPIStatus
|
void WUPSConfigItemStub_onButtonPressed(void *context, WUPSConfigButtons buttons) {
|
||||||
WUPSConfigItemStub_Create(const char *displayName, WUPSConfigItemHandle *outHandle) {
|
}
|
||||||
if (displayName == nullptr || outHandle == nullptr) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
bool WUPSConfigItemStub_isMovementAllowed(void *context) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t WUPSConfigItemStub_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) {
|
||||||
|
memset(out_buf, 0, out_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WUPSConfigItemStub_restoreDefault(void *context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WUPSConfigItemStub_onSelected(void *context, bool isSelected) {
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" bool
|
||||||
|
WUPSConfigItemStub_AddToCategoryEx(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName) {
|
||||||
|
if (cat == 0) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *item = (ConfigItemStub *) malloc(sizeof(ConfigItemStub));
|
auto *item = (ConfigItemStub *) malloc(sizeof(ConfigItemStub));
|
||||||
if (item == nullptr) {
|
if (item == nullptr) {
|
||||||
return WUPSCONFIG_API_RESULT_OUT_OF_MEMORY;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WUPSConfigAPIItemCallbacksV2 callbacks = {
|
WUPSConfigCallbacks_t callbacks = {
|
||||||
.getCurrentValueDisplay = &WUPSConfigItemStub_getEmptyTextValue,
|
.getCurrentValueDisplay = &WUPSConfigItemStub_getCurrentValueDisplay,
|
||||||
.getCurrentValueSelectedDisplay = &WUPSConfigItemStub_getEmptyTextValue,
|
.getCurrentValueSelectedDisplay = &WUPSConfigItemStub_getCurrentValueSelectedDisplay,
|
||||||
.onSelected = nullptr,
|
.onSelected = &WUPSConfigItemStub_onSelected,
|
||||||
.restoreDefault = nullptr,
|
.restoreDefault = &WUPSConfigItemStub_restoreDefault,
|
||||||
.isMovementAllowed = nullptr,
|
.isMovementAllowed = &WUPSConfigItemStub_isMovementAllowed,
|
||||||
.onCloseCallback = nullptr,
|
.callCallback = &WUPSConfigItemStub_callCallback,
|
||||||
.onInput = nullptr,
|
.onButtonPressed = &WUPSConfigItemStub_onButtonPressed,
|
||||||
.onInputEx = nullptr,
|
.onDelete = &WUPSConfigItemStub_onDelete};
|
||||||
.onDelete = &WUPSConfigItemStub_Cleanup,
|
|
||||||
};
|
|
||||||
|
|
||||||
WUPSConfigAPIItemOptionsV2 options = {
|
if (WUPSConfigItem_Create(&item->handle, configID, displayName, callbacks, item) < 0) {
|
||||||
.displayName = displayName,
|
free(item);
|
||||||
.context = item,
|
return false;
|
||||||
.callbacks = callbacks,
|
|
||||||
};
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus err;
|
|
||||||
if ((err = WUPSConfigAPI_Item_Create(options, &item->handle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
WUPSConfigItemStub_Cleanup(item);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*outHandle = item->handle;
|
if (WUPSConfigCategory_AddItem(cat, item->handle) < 0) {
|
||||||
|
WUPSConfigItem_Destroy(item->handle);
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" WUPSConfigAPIStatus
|
void WUPSConfigItemStub_onDelete(void *context) {
|
||||||
WUPSConfigItemStub_AddToCategory(WUPSConfigCategoryHandle cat, const char *displayName) {
|
auto *item = (ConfigItemStub *) context;
|
||||||
WUPSConfigItemHandle itemHandle;
|
free(item);
|
||||||
WUPSConfigAPIStatus res;
|
}
|
||||||
if ((res = WUPSConfigItemStub_Create(displayName,
|
|
||||||
&itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
extern "C" bool WUPSConfigItemStub_AddToCategory(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName) {
|
||||||
return res;
|
return WUPSConfigItemStub_AddToCategoryEx(cat, configID, displayName);
|
||||||
}
|
}
|
||||||
if ((res = WUPSConfigAPI_Category_AddItem(cat, itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
WUPSConfigAPI_Item_Destroy(itemHandle);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
#include "wups/config/WUPSConfigItemStub.h"
|
|
||||||
|
|
||||||
std::optional<WUPSConfigItemStub> WUPSConfigItemStub::Create(std::string_view displayName,
|
|
||||||
WUPSConfigAPIStatus &err) noexcept {
|
|
||||||
WUPSConfigItemHandle itemHandle;
|
|
||||||
if ((err = WUPSConfigItemStub_Create(displayName.data(),
|
|
||||||
&itemHandle)) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return WUPSConfigItemStub(itemHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigItemStub WUPSConfigItemStub::Create(std::string_view displayName) {
|
|
||||||
WUPSConfigAPIStatus err = WUPSCONFIG_API_RESULT_UNKNOWN_ERROR;
|
|
||||||
auto result = Create(displayName, err);
|
|
||||||
if (!result) {
|
|
||||||
throw std::runtime_error(std::string("Failed to create WUPSConfigItemStub: ").append(WUPSConfigAPI_GetStatusStr(err)));
|
|
||||||
}
|
|
||||||
return std::move(*result);
|
|
||||||
}
|
|
20
libraries/libwups/config.def
Normal file
20
libraries/libwups/config.def
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
:NAME homebrew_wupsbackend
|
||||||
|
|
||||||
|
:TEXT
|
||||||
|
WUPSConfigItem_Create
|
||||||
|
WUPSConfigItem_Destroy
|
||||||
|
WUPSConfigItem_SetDisplayName
|
||||||
|
WUPSConfigItem_GetDisplayName
|
||||||
|
WUPSConfigItem_SetConfigID
|
||||||
|
WUPSConfigItem_GetConfigID
|
||||||
|
|
||||||
|
WUPSConfig_Create
|
||||||
|
WUPSConfig_Destroy
|
||||||
|
WUPSConfig_GetName
|
||||||
|
WUPSConfig_AddCategoryByName
|
||||||
|
WUPSConfig_AddCategory
|
||||||
|
|
||||||
|
WUPSConfigCategory_Create
|
||||||
|
WUPSConfigCategory_Destroy
|
||||||
|
WUPSConfigCategory_GetName
|
||||||
|
WUPSConfigCategory_AddItem
|
@ -1,245 +0,0 @@
|
|||||||
#include "wups/config_api.h"
|
|
||||||
#include "wups/wups_debug.h"
|
|
||||||
#include <coreinit/dynload.h>
|
|
||||||
#include <wups/config.h>
|
|
||||||
|
|
||||||
static OSDynLoad_Module sModuleHandle = nullptr;
|
|
||||||
|
|
||||||
static WUPSConfigAPIStatus (*sAPIGetVersion)(WUPSConfigAPIVersion *out) = nullptr;
|
|
||||||
static WUPSConfigAPIStatus (*sAPIInitEx)(uint32_t pluginIdentifier, WUPSConfigAPIOptions options, WUPSConfigAPI_MenuOpenedCallback openedCallback, WUPSConfigAPI_MenuClosedCallback closedCallback) = nullptr;
|
|
||||||
static WUPSConfigAPIStatus (*sAPICategoryCreateEx)(WUPSConfigAPICreateCategoryOptions options, WUPSConfigCategoryHandle *out) = nullptr;
|
|
||||||
static WUPSConfigAPIStatus (*sAPICategoryDestroy)(WUPSConfigCategoryHandle handle) = nullptr;
|
|
||||||
static WUPSConfigAPIStatus (*sAPICategoryAddCategory)(WUPSConfigCategoryHandle parentHandle, WUPSConfigCategoryHandle categoryHandle) = nullptr;
|
|
||||||
static WUPSConfigAPIStatus (*sAPICategoryAddItem)(WUPSConfigCategoryHandle parentHandle, WUPSConfigItemHandle itemHandle) = nullptr;
|
|
||||||
static WUPSConfigAPIStatus (*sAPIItemCreateEx)(WUPSConfigAPICreateItemOptions options, WUPSConfigItemHandle *out) = nullptr;
|
|
||||||
static WUPSConfigAPIStatus (*sAPIItemDestroy)(WUPSConfigItemHandle handle) = nullptr;
|
|
||||||
|
|
||||||
static WUPSConfigAPIVersion sConfigAPIVersion = WUPS_CONFIG_API_VERSION_ERROR;
|
|
||||||
|
|
||||||
static bool sConfigLibInitDone = false;
|
|
||||||
static uint32_t sConfigPluginIdentifier = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
const char *WUPSConfigAPI_GetStatusStr(WUPSConfigAPIStatus status) {
|
|
||||||
switch (status) {
|
|
||||||
case WUPSCONFIG_API_RESULT_SUCCESS:
|
|
||||||
return "WUPSCONFIG_API_RESULT_SUCCESS";
|
|
||||||
case WUPSCONFIG_API_RESULT_INVALID_ARGUMENT:
|
|
||||||
return "WUPSCONFIG_API_RESULT_INVALID_ARGUMENT";
|
|
||||||
case WUPSCONFIG_API_RESULT_OUT_OF_MEMORY:
|
|
||||||
return "WUPSCONFIG_API_RESULT_OUT_OF_MEMORY";
|
|
||||||
case WUPSCONFIG_API_RESULT_NOT_FOUND:
|
|
||||||
return "WUPSCONFIG_API_RESULT_NOT_FOUND";
|
|
||||||
case WUPSCONFIG_API_RESULT_MODULE_NOT_FOUND:
|
|
||||||
return "WUPSCONFIG_API_RESULT_MODULE_NOT_FOUND";
|
|
||||||
case WUPSCONFIG_API_RESULT_INVALID_PLUGIN_IDENTIFIER:
|
|
||||||
return "WUPSCONFIG_API_RESULT_INVALID_PLUGIN_IDENTIFIER";
|
|
||||||
case WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT:
|
|
||||||
return "WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT";
|
|
||||||
case WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION:
|
|
||||||
return "WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION";
|
|
||||||
case WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND:
|
|
||||||
return "WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND";
|
|
||||||
case WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED:
|
|
||||||
return "WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED";
|
|
||||||
case WUPSCONFIG_API_RESULT_UNKNOWN_ERROR:
|
|
||||||
return "WUPSCONFIG_API_RESULT_UNKNOWN_ERROR";
|
|
||||||
case WUPSCONFIG_API_RESULT_MISSING_CALLBACK:
|
|
||||||
return "WUPSCONFIG_API_RESULT_MISSING_CALLBACK";
|
|
||||||
}
|
|
||||||
return "WUPSCONFIG_API_RESULT_UNKNOWN_ERROR";
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" WUPSConfigAPIStatus WUPSConfigAPI_InitLibrary_Internal(wups_loader_init_config_args_t args) {
|
|
||||||
if (sConfigLibInitDone) {
|
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
if (args.arg_version != 1) {
|
|
||||||
return WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OSDynLoad_Acquire("homebrew_wupsbackend", &sModuleHandle) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libuwps: OSDynLoad_Acquire \"homebrew_wupsbackend\" failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OSDynLoad_FindExport(sModuleHandle, OS_DYNLOAD_EXPORT_FUNC, "WUPSConfigAPI_GetVersion", (void **) &sAPIGetVersion) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libuwps: FindExport WUPSConfigAPI_GetVersion failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT;
|
|
||||||
}
|
|
||||||
auto res = WUPSConfigAPI_GetVersion(&sConfigAPIVersion);
|
|
||||||
if (res != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return WUPSCONFIG_API_RESULT_UNSUPPORTED_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OSDynLoad_FindExport(sModuleHandle, OS_DYNLOAD_EXPORT_FUNC, "WUPSConfigAPI_InitEx", (void **) &sAPIInitEx) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libwups: FindExport WUPSConfigAPI_InitEx failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT;
|
|
||||||
}
|
|
||||||
if (OSDynLoad_FindExport(sModuleHandle, OS_DYNLOAD_EXPORT_FUNC, "WUPSConfigAPI_Category_CreateEx", (void **) &sAPICategoryCreateEx) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libwups: FindExport WUPSConfigAPI_Category_CreateEx failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OSDynLoad_FindExport(sModuleHandle, OS_DYNLOAD_EXPORT_FUNC, "WUPSConfigAPI_Category_Destroy", (void **) &sAPICategoryDestroy) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libwups: FindExport WUPSConfigAPI_Category_Destroy failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT;
|
|
||||||
}
|
|
||||||
if (OSDynLoad_FindExport(sModuleHandle, OS_DYNLOAD_EXPORT_FUNC, "WUPSConfigAPI_Category_AddCategory", (void **) &sAPICategoryAddCategory) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libwups: FindExport WUPSConfigAPI_Category_AddCategory failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT;
|
|
||||||
}
|
|
||||||
if (OSDynLoad_FindExport(sModuleHandle, OS_DYNLOAD_EXPORT_FUNC, "WUPSConfigAPI_Category_AddItem", (void **) &sAPICategoryAddItem) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libwups: FindExport WUPSConfigAPI_Category_AddItem failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OSDynLoad_FindExport(sModuleHandle, OS_DYNLOAD_EXPORT_FUNC, "WUPSConfigAPI_Item_CreateEx", (void **) &sAPIItemCreateEx) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libwups: FindExport WUPSConfigAPI_Item_Create failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT;
|
|
||||||
}
|
|
||||||
if (OSDynLoad_FindExport(sModuleHandle, OS_DYNLOAD_EXPORT_FUNC, "WUPSConfigAPI_Item_Destroy", (void **) &sAPIItemDestroy) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libwups: FindExport WUPSConfigAPI_Item_Destroy failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
sConfigLibInitDone = true;
|
|
||||||
sConfigPluginIdentifier = args.plugin_identifier;
|
|
||||||
|
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" WUPSConfigAPIStatus WUPSConfigAPI_DeInitLibrary_Internal() {
|
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_GetVersion(WUPSConfigAPIVersion *outVariable) {
|
|
||||||
if (sAPIGetVersion == nullptr) {
|
|
||||||
if (OSDynLoad_Acquire("homebrew_notifications", &sModuleHandle) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libwups: OSDynLoad_Acquire failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OSDynLoad_FindExport(sModuleHandle, OS_DYNLOAD_EXPORT_FUNC, "WUPSConfigAPI_GetVersion", (void **) &sAPIGetVersion) != OS_DYNLOAD_OK) {
|
|
||||||
WUPS_DEBUG_REPORT("libwups: FindExport WUPSConfigAPI_GetVersion failed.\n");
|
|
||||||
return WUPSCONFIG_API_RESULT_MODULE_MISSING_EXPORT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sAPIGetVersion(outVariable);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_InitEx(uint32_t pluginIdentifier, WUPSConfigAPIOptions options, WUPSConfigAPI_MenuOpenedCallback openedCallback, WUPSConfigAPI_MenuClosedCallback closedCallback) {
|
|
||||||
if (sConfigAPIVersion == WUPS_CONFIG_API_VERSION_ERROR) {
|
|
||||||
return WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED;
|
|
||||||
}
|
|
||||||
if (sAPIInitEx == nullptr || sConfigAPIVersion < 1) {
|
|
||||||
return WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (openedCallback == nullptr || closedCallback == nullptr || options.version != 1) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sAPIInitEx(pluginIdentifier, options, openedCallback, closedCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Init(WUPSConfigAPIOptionsV1 optionsV1, WUPSConfigAPI_MenuOpenedCallback openedCallback, WUPSConfigAPI_MenuClosedCallback closedCallback) {
|
|
||||||
if (sConfigPluginIdentifier == 0xFFFFFFFF) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_PLUGIN_IDENTIFIER;
|
|
||||||
}
|
|
||||||
WUPSConfigAPIOptions options = {
|
|
||||||
.version = WUPS_API_CONFIG_API_OPTION_VERSION_V1,
|
|
||||||
.data = {.v1 = optionsV1},
|
|
||||||
};
|
|
||||||
return WUPSConfigAPI_InitEx(sConfigPluginIdentifier, options, openedCallback, closedCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Category_CreateEx(WUPSConfigAPICreateCategoryOptions options, WUPSConfigCategoryHandle *out) {
|
|
||||||
if (sConfigAPIVersion == WUPS_CONFIG_API_VERSION_ERROR) {
|
|
||||||
return WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED;
|
|
||||||
}
|
|
||||||
if (sAPICategoryCreateEx == nullptr || sConfigAPIVersion < 1) {
|
|
||||||
return WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out == nullptr) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sAPICategoryCreateEx(options, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Category_Destroy(WUPSConfigCategoryHandle handle) {
|
|
||||||
if (sConfigAPIVersion == WUPS_CONFIG_API_VERSION_ERROR) {
|
|
||||||
return WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED;
|
|
||||||
}
|
|
||||||
if (sAPICategoryDestroy == nullptr || sConfigAPIVersion < 1) {
|
|
||||||
return WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle == nullptr) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sAPICategoryDestroy(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Category_AddCategory(WUPSConfigCategoryHandle parentHandle, WUPSConfigCategoryHandle categoryHandle) {
|
|
||||||
if (sConfigAPIVersion == WUPS_CONFIG_API_VERSION_ERROR) {
|
|
||||||
return WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED;
|
|
||||||
}
|
|
||||||
if (sAPICategoryAddCategory == nullptr || sConfigAPIVersion < 1) {
|
|
||||||
return WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentHandle == nullptr || categoryHandle == nullptr) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sAPICategoryAddCategory(parentHandle, categoryHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Category_AddItem(WUPSConfigCategoryHandle parentHandle, WUPSConfigItemHandle itemHandle) {
|
|
||||||
if (sConfigAPIVersion == WUPS_CONFIG_API_VERSION_ERROR) {
|
|
||||||
return WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED;
|
|
||||||
}
|
|
||||||
if (sAPICategoryAddItem == nullptr || sConfigAPIVersion < 1) {
|
|
||||||
return WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentHandle == 0 || itemHandle == 0) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sAPICategoryAddItem(parentHandle, itemHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Item_CreateEx(WUPSConfigAPICreateItemOptions options, WUPSConfigItemHandle *out) {
|
|
||||||
if (sConfigAPIVersion == WUPS_CONFIG_API_VERSION_ERROR) {
|
|
||||||
return WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED;
|
|
||||||
}
|
|
||||||
if (sAPIItemCreateEx == nullptr || sConfigAPIVersion < 1) {
|
|
||||||
return WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out == nullptr) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sAPIItemCreateEx(options, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPIStatus WUPSConfigAPI_Item_Destroy(WUPSConfigItemHandle handle) {
|
|
||||||
if (sConfigAPIVersion == WUPS_CONFIG_API_VERSION_ERROR) {
|
|
||||||
return WUPSCONFIG_API_RESULT_LIB_UNINITIALIZED;
|
|
||||||
}
|
|
||||||
if (sAPIItemDestroy == nullptr || sConfigAPIVersion < 1) {
|
|
||||||
return WUPSCONFIG_API_RESULT_UNSUPPORTED_COMMAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle == nullptr) {
|
|
||||||
return WUPSCONFIG_API_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sAPIItemDestroy(handle);
|
|
||||||
}
|
|
@ -1,8 +1,6 @@
|
|||||||
#include "wups_reent.h"
|
#include "wups_reent.h"
|
||||||
#include "wups_thread_specific.h"
|
#include "wups_thread_specific.h"
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
extern "C" void OSFatal(const char *);
|
extern "C" void OSFatal(const char *);
|
||||||
@ -55,60 +53,3 @@ extern "C" void *__attribute__((weak)) wut_get_thread_specific(__wut_thread_spec
|
|||||||
void *wut_get_thread_specific(__wut_thread_specific_id id) {
|
void *wut_get_thread_specific(__wut_thread_specific_id id) {
|
||||||
return wups_get_thread_specific(id);
|
return wups_get_thread_specific(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" const char wups_meta_plugin_name[];
|
|
||||||
extern "C" void __attribute__((weak)) abort(void);
|
|
||||||
extern "C" void __attribute__((weak)) __assert_func(const char *file, int line, const char *func, const char *failedexpr);
|
|
||||||
extern "C" void __attribute__((weak)) __assert(const char *file, int line, const char *failedexpr);
|
|
||||||
|
|
||||||
void __attribute__((weak))
|
|
||||||
abort(void) {
|
|
||||||
char buffer[512] = {};
|
|
||||||
strcat(buffer, "Wii U Plugin System (plugin: \"");
|
|
||||||
strcat(buffer, wups_meta_plugin_name);
|
|
||||||
strcat(buffer, "\"):\n Abort called. Uncaught exception?");
|
|
||||||
OSFatal(buffer);
|
|
||||||
/* NOTREACHED */
|
|
||||||
while (1)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
void __attribute__((weak))
|
|
||||||
__assert_func(const char *file,
|
|
||||||
int line,
|
|
||||||
const char *func,
|
|
||||||
const char *failedexpr) {
|
|
||||||
char tmp[512] = {};
|
|
||||||
char buffer[512] = {};
|
|
||||||
|
|
||||||
snprintf(tmp, sizeof(tmp), "Wii U Plugin System (plugin: \"%s\"):\n\n"
|
|
||||||
"assertion \"%s\" failed:\n\n"
|
|
||||||
"file \"%s\", line %d%s%s",
|
|
||||||
wups_meta_plugin_name, failedexpr, file, line, func ? ", function: " : "", func ? func : "");
|
|
||||||
|
|
||||||
// make sure to add a \n every 64 characters to fit on the DRC screen.
|
|
||||||
char *target_ptr = buffer;
|
|
||||||
int i = 0, j = 0, lineLength = 0;
|
|
||||||
while (tmp[i] != '\0' && j < (int) sizeof(buffer) - 2) {
|
|
||||||
if (tmp[i] == '\n') {
|
|
||||||
lineLength = 0;
|
|
||||||
} else if (lineLength >= 64) {
|
|
||||||
target_ptr[j++] = '\n';
|
|
||||||
lineLength = 0;
|
|
||||||
}
|
|
||||||
target_ptr[j++] = tmp[i++];
|
|
||||||
lineLength++;
|
|
||||||
}
|
|
||||||
|
|
||||||
OSFatal(buffer);
|
|
||||||
/* NOTREACHED */
|
|
||||||
}
|
|
||||||
|
|
||||||
void __attribute__((weak))
|
|
||||||
__assert(const char *file,
|
|
||||||
int line,
|
|
||||||
const char *failedexpr) {
|
|
||||||
__assert_func(file, line, NULL, failedexpr);
|
|
||||||
/* NOTREACHED */
|
|
||||||
}
|
|
||||||
|
@ -1,158 +1,633 @@
|
|||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <wups/storage.h>
|
#include <wups.h>
|
||||||
|
|
||||||
struct wups_internal_functions_t {
|
#include "utils/base64.h"
|
||||||
WUPSStorage_SaveFunction save_function_ptr = nullptr;
|
|
||||||
WUPSStorage_ForceReloadFunction force_reload_function_ptr = nullptr;
|
|
||||||
WUPSStorage_WipeStorageFunction wipe_storage_function_ptr = nullptr;
|
|
||||||
WUPSStorage_DeleteItemFunction delete_item_function_ptr = nullptr;
|
|
||||||
WUPSStorage_CreateSubItemFunction create_sub_item_function_ptr = nullptr;
|
|
||||||
WUPSStorage_GetSubItemFunction get_sub_item_function_ptr = nullptr;
|
|
||||||
WUPSStorage_StoreItemFunction store_item_function_ptr = nullptr;
|
|
||||||
WUPSStorage_GetItemFunction get_item_function_ptr = nullptr;
|
|
||||||
WUPSStorage_GetItemSizeFunction get_item_size_function_ptr = nullptr;
|
|
||||||
wups_storage_root_item __storageroot_item = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
static wups_internal_functions_t __internal_functions __attribute__((section(".data"))) = {};
|
static OpenStorageFunction openfunction_ptr __attribute__((section(".data"))) = nullptr;
|
||||||
|
static CloseStorageFunction closefunction_ptr __attribute__((section(".data"))) = nullptr;
|
||||||
|
static char plugin_id[256] __attribute__((section(".data")));
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI_InitInternal(wups_loader_init_storage_args_t args) {
|
static uint32_t storage_initialized __attribute__((section(".data"))) = false;
|
||||||
if (args.version > WUPS_STORAGE_CUR_API_VERSION) {
|
static uint32_t isOpened __attribute__((section(".data")));
|
||||||
__internal_functions = {};
|
static uint32_t isDirty __attribute__((section(".data")));
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_INVALID_VERSION;
|
static wups_storage_item_t rootItem __attribute__((section(".data")));
|
||||||
}
|
|
||||||
__internal_functions.__storageroot_item = args.root_item;
|
|
||||||
__internal_functions.save_function_ptr = args.save_function_ptr;
|
|
||||||
__internal_functions.force_reload_function_ptr = args.force_reload_function_ptr;
|
|
||||||
__internal_functions.wipe_storage_function_ptr = args.wipe_storage_function_ptr;
|
|
||||||
__internal_functions.delete_item_function_ptr = args.delete_item_function_ptr;
|
|
||||||
__internal_functions.create_sub_item_function_ptr = args.create_sub_item_function_ptr;
|
|
||||||
__internal_functions.get_sub_item_function_ptr = args.get_sub_item_function_ptr;
|
|
||||||
__internal_functions.store_item_function_ptr = args.store_item_function_ptr;
|
|
||||||
__internal_functions.get_item_function_ptr = args.get_item_function_ptr;
|
|
||||||
__internal_functions.get_item_size_function_ptr = args.get_item_size_function_ptr;
|
|
||||||
|
|
||||||
return WUPS_STORAGE_ERROR_SUCCESS;
|
static wups_storage_item_t *sActiveSubItem __attribute__((section(".data"))) = nullptr;
|
||||||
|
|
||||||
|
void WUPS_InitStorage(wups_loader_init_storage_args_t args) {
|
||||||
|
openfunction_ptr = args.open_storage_ptr;
|
||||||
|
closefunction_ptr = args.close_storage_ptr;
|
||||||
|
strncpy(plugin_id, args.plugin_id, sizeof(plugin_id) - 1);
|
||||||
|
|
||||||
|
storage_initialized = true;
|
||||||
|
isOpened = false;
|
||||||
|
isDirty = false;
|
||||||
|
sActiveSubItem = nullptr;
|
||||||
|
|
||||||
|
rootItem.key = nullptr;
|
||||||
|
rootItem.data = nullptr;
|
||||||
|
rootItem.data_size = 0;
|
||||||
|
rootItem.deleted = false;
|
||||||
|
rootItem.type = WUPS_STORAGE_TYPE_ITEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *WUPSStorageAPI_GetStatusStr(WUPSStorageError status) {
|
const char *WUPS_GetStorageStatusStr(WUPSStorageError status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case WUPS_STORAGE_ERROR_SUCCESS:
|
case WUPS_STORAGE_ERROR_SUCCESS:
|
||||||
return "WUPS_STORAGE_ERROR_SUCCESS";
|
return "WUPS_STORAGE_ERROR_SUCCESS";
|
||||||
|
case WUPS_STORAGE_ERROR_NOT_OPENED:
|
||||||
|
return "WUPS_STORAGE_ERROR_NOT_OPENED";
|
||||||
|
case WUPS_STORAGE_ERROR_ALREADY_OPENED:
|
||||||
|
return "WUPS_STORAGE_ERROR_ALREADY_OPENED";
|
||||||
case WUPS_STORAGE_ERROR_INVALID_ARGS:
|
case WUPS_STORAGE_ERROR_INVALID_ARGS:
|
||||||
return "WUPS_STORAGE_ERROR_INVALID_ARGS";
|
return "WUPS_STORAGE_ERROR_INVALID_ARGS";
|
||||||
case WUPS_STORAGE_ERROR_MALLOC_FAILED:
|
|
||||||
return "WUPS_STORAGE_ERROR_MALLOC_FAILED";
|
|
||||||
case WUPS_STORAGE_ERROR_UNEXPECTED_DATA_TYPE:
|
|
||||||
return "WUPS_STORAGE_ERROR_UNEXPECTED_DATA_TYPE";
|
|
||||||
case WUPS_STORAGE_ERROR_BUFFER_TOO_SMALL:
|
|
||||||
return "WUPS_STORAGE_ERROR_BUFFER_TOO_SMALL";
|
|
||||||
case WUPS_STORAGE_ERROR_ALREADY_EXISTS:
|
|
||||||
return "WUPS_STORAGE_ERROR_ALREADY_EXISTS";
|
|
||||||
case WUPS_STORAGE_ERROR_IO_ERROR:
|
|
||||||
return "WUPS_STORAGE_ERROR_IO_ERROR";
|
|
||||||
case WUPS_STORAGE_ERROR_NOT_FOUND:
|
case WUPS_STORAGE_ERROR_NOT_FOUND:
|
||||||
return "WUPS_STORAGE_ERROR_NOT_FOUND";
|
return "WUPS_STORAGE_ERROR_NOT_FOUND";
|
||||||
case WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED:
|
case WUPS_STORAGE_ERROR_NOT_INITIALIZED:
|
||||||
return "WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED";
|
return "WUPS_STORAGE_ERROR_NOT_INITIALIZED";
|
||||||
case WUPS_STORAGE_ERROR_INTERNAL_INVALID_VERSION:
|
case WUPS_STORAGE_ERROR_INVALID_BACKEND_PARAMS:
|
||||||
return "WUPS_STORAGE_ERROR_INTERNAL_INVALID_VERSION";
|
return "WUPS_STORAGE_ERROR_INVALID_BACKEND_PARAMS";
|
||||||
case WUPS_STORAGE_ERROR_UNKNOWN_ERROR:
|
case WUPS_STORAGE_ERROR_INVALID_JSON:
|
||||||
return "WUPS_STORAGE_ERROR_UNKNOWN_ERROR";
|
return "WUPS_STORAGE_ERROR_INVALID_JSON";
|
||||||
|
case WUPS_STORAGE_ERROR_IO:
|
||||||
|
return "WUPS_STORAGE_ERROR_IO";
|
||||||
|
case WUPS_STORAGE_ERROR_B64_DECODE_FAILED:
|
||||||
|
return "WUPS_STORAGE_ERROR_B64_DECODE_FAILED";
|
||||||
|
case WUPS_STORAGE_ERROR_BUFFER_TOO_SMALL:
|
||||||
|
return "WUPS_STORAGE_ERROR_BUFFER_TOO_SMALL";
|
||||||
|
case WUPS_STORAGE_ERROR_MALLOC_FAILED:
|
||||||
|
return "WUPS_STORAGE_ERROR_MALLOC_FAILED";
|
||||||
|
case WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY:
|
||||||
|
return "WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY";
|
||||||
}
|
}
|
||||||
return "WUPS_STORAGE_ERROR_UNKNOWN";
|
return "WUPS_STORAGE_ERROR_UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI_SaveStorage(bool force) {
|
WUPSStorageError WUPS_OpenStorage(void) {
|
||||||
if (__internal_functions.save_function_ptr == nullptr) {
|
if (!storage_initialized) {
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
if (__internal_functions.__storageroot_item == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
if (isOpened) {
|
||||||
|
return WUPS_STORAGE_ERROR_ALREADY_OPENED;
|
||||||
}
|
}
|
||||||
return __internal_functions.save_function_ptr(__internal_functions.__storageroot_item, force);
|
|
||||||
|
WUPSStorageError result = openfunction_ptr(plugin_id, &rootItem);
|
||||||
|
|
||||||
|
if (result == WUPS_STORAGE_ERROR_SUCCESS || result == WUPS_STORAGE_ERROR_INVALID_JSON) {
|
||||||
|
isOpened = true;
|
||||||
|
isDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI_ForceReloadStorage() {
|
static void closeItem(wups_storage_item_t *item) {
|
||||||
if (__internal_functions.force_reload_function_ptr == nullptr) {
|
if (!item) {
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
return;
|
||||||
}
|
}
|
||||||
if (__internal_functions.__storageroot_item == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
if (item->type == WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
auto *items = (wups_storage_item_t *) item->data;
|
||||||
|
for (uint32_t i = 0; i < item->data_size; i++) {
|
||||||
|
closeItem(&items[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return __internal_functions.force_reload_function_ptr(__internal_functions.__storageroot_item);
|
free(item->data);
|
||||||
|
free(item->key);
|
||||||
}
|
}
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI_WipeStorage() {
|
WUPSStorageError WUPS_CloseStorage(void) {
|
||||||
if (__internal_functions.wipe_storage_function_ptr == nullptr) {
|
if (!storage_initialized) {
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
if (__internal_functions.__storageroot_item == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
if (!isOpened) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
}
|
}
|
||||||
return __internal_functions.wipe_storage_function_ptr(__internal_functions.__storageroot_item);
|
|
||||||
|
WUPSStorageError result = WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
if (isDirty) {
|
||||||
|
result = closefunction_ptr(plugin_id, &rootItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == WUPS_STORAGE_ERROR_SUCCESS) {
|
||||||
|
isOpened = false;
|
||||||
|
isDirty = false;
|
||||||
|
|
||||||
|
closeItem(&rootItem);
|
||||||
|
rootItem.data_size = 0;
|
||||||
|
rootItem.data = nullptr;
|
||||||
|
rootItem.key = nullptr;
|
||||||
|
}
|
||||||
|
sActiveSubItem = nullptr;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI_DeleteItem(wups_storage_item parent, const char *key) {
|
WUPSStorageError WUPS_DeleteItem(wups_storage_item_t *parent, const char *key) {
|
||||||
if (__internal_functions.delete_item_function_ptr == nullptr) {
|
if (!storage_initialized) {
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
if (__internal_functions.__storageroot_item == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
|
||||||
}
|
|
||||||
return __internal_functions.delete_item_function_ptr(__internal_functions.__storageroot_item, parent, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI_CreateSubItem(wups_storage_item parent, const char *key, wups_storage_item *outItem) {
|
if (!isOpened) {
|
||||||
if (__internal_functions.create_sub_item_function_ptr == nullptr) {
|
return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
|
||||||
}
|
}
|
||||||
if (__internal_functions.__storageroot_item == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
|
||||||
}
|
|
||||||
return __internal_functions.create_sub_item_function_ptr(__internal_functions.__storageroot_item, parent, key, outItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI_GetSubItem(wups_storage_item parent, const char *key, wups_storage_item *outItem) {
|
if (!key) {
|
||||||
if (__internal_functions.get_sub_item_function_ptr == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
|
||||||
}
|
|
||||||
if (__internal_functions.__storageroot_item == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
|
||||||
}
|
|
||||||
return __internal_functions.get_sub_item_function_ptr(__internal_functions.__storageroot_item, parent, key, outItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI_StoreItem(wups_storage_item parent, const char *key, WUPSStorageItemType type, void *data, uint32_t size) {
|
|
||||||
if (__internal_functions.store_item_function_ptr == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
|
||||||
}
|
|
||||||
if (__internal_functions.__storageroot_item == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
|
||||||
}
|
|
||||||
return __internal_functions.store_item_function_ptr(__internal_functions.__storageroot_item, parent, key, type, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI_GetItem(wups_storage_item parent, const char *key, WUPSStorageItemType type, void *data, uint32_t maxSize, uint32_t *outSize) {
|
|
||||||
if (data == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
}
|
}
|
||||||
if (__internal_functions.get_item_function_ptr == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
if (!parent) {
|
||||||
|
parent = &rootItem;
|
||||||
|
} else {
|
||||||
|
// We can only safely process items of a parent if the parent was the last
|
||||||
|
// item returned by WUPS_GetSubItem or WUPS_CreateSubItem
|
||||||
|
if (parent != sActiveSubItem) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY;
|
||||||
|
}
|
||||||
|
if (parent->type != WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (__internal_functions.__storageroot_item == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
isDirty = true;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < parent->data_size; i++) {
|
||||||
|
wups_storage_item_t *item = &((wups_storage_item_t *) parent->data)[i];
|
||||||
|
|
||||||
|
if (item->deleted || item->type == WUPS_STORAGE_TYPE_INVALID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(item->key, key) == 0) {
|
||||||
|
free(item->data);
|
||||||
|
free(item->key);
|
||||||
|
item->key = nullptr;
|
||||||
|
item->data = nullptr;
|
||||||
|
item->deleted = true;
|
||||||
|
if (sActiveSubItem == item) {
|
||||||
|
sActiveSubItem = nullptr;
|
||||||
|
}
|
||||||
|
return WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return __internal_functions.get_item_function_ptr(__internal_functions.__storageroot_item, parent, key, type, data, maxSize, outSize);
|
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// int32_t WUPS_GetSize(const char* key) {
|
||||||
|
// if (!storage_initialized) {
|
||||||
|
// return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
|
// }
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI_GetItemSize(wups_storage_item parent, const char *key, WUPSStorageItemType itemType, uint32_t *outSize) {
|
// if (!isOpened) {
|
||||||
if (outSize == nullptr) {
|
// return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (uint32_t i = 0; i < amount_of_items; i++) {
|
||||||
|
// wups_loader_storage_item_t* item = &items[i];
|
||||||
|
|
||||||
|
// if (item->pending_delete || item->type == WUPS_STORAGE_TYPE_INVALID) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (strcmp(item->key, key) == 0) {
|
||||||
|
// return item->data_size;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return WUPS_STORAGE_ERROR_NOT_FOUND;
|
||||||
|
// }
|
||||||
|
|
||||||
|
static wups_storage_item_t *addItem(wups_storage_item_t *parent, const char *key, wups_storage_type_t type, WUPSStorageError *error) {
|
||||||
|
wups_storage_item_t *foundItem = nullptr;
|
||||||
|
// First check for existing item with the same name.
|
||||||
|
for (uint32_t i = 0; i < parent->data_size; i++) {
|
||||||
|
wups_storage_item_t *item = &((wups_storage_item_t *) parent->data)[i];
|
||||||
|
|
||||||
|
if (item->key && strcmp(item->key, key) == 0) {
|
||||||
|
free(item->data);
|
||||||
|
foundItem = item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundItem) {
|
||||||
|
// Then check if there are any deleted item we can override.
|
||||||
|
for (uint32_t i = 0; i < parent->data_size; i++) {
|
||||||
|
wups_storage_item_t *item = &((wups_storage_item_t *) parent->data)[i];
|
||||||
|
|
||||||
|
if (item->deleted) {
|
||||||
|
free(item->data);
|
||||||
|
free(item->key);
|
||||||
|
item->data = nullptr;
|
||||||
|
item->key = nullptr;
|
||||||
|
|
||||||
|
item->key = strdup(key);
|
||||||
|
if (item->key == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
foundItem = item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundItem) {
|
||||||
|
auto *newPtr = (wups_storage_item_t *) realloc(parent->data, (parent->data_size + 1) * sizeof(wups_storage_item_t));
|
||||||
|
if (newPtr == nullptr) {
|
||||||
|
*error = WUPS_STORAGE_ERROR_MALLOC_FAILED;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
parent->data = newPtr;
|
||||||
|
|
||||||
|
foundItem = &((wups_storage_item_t *) parent->data)[parent->data_size];
|
||||||
|
memset(foundItem, 0, sizeof(wups_storage_item_t));
|
||||||
|
foundItem->deleted = true;
|
||||||
|
|
||||||
|
parent->data_size += 1;
|
||||||
|
|
||||||
|
foundItem->key = strdup(key);
|
||||||
|
if (foundItem->key == nullptr) {
|
||||||
|
*error = WUPS_STORAGE_ERROR_MALLOC_FAILED;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foundItem->type = type;
|
||||||
|
foundItem->deleted = false;
|
||||||
|
foundItem->data = nullptr;
|
||||||
|
foundItem->data_size = 0;
|
||||||
|
return foundItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSStorageError WUPS_CreateSubItem(wups_storage_item_t *parent, const char *key, wups_storage_item_t **outItem) {
|
||||||
|
if (!storage_initialized) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpened) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key || !outItem) {
|
||||||
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
}
|
}
|
||||||
if (__internal_functions.get_item_size_function_ptr == nullptr) {
|
|
||||||
return WUPS_STORAGE_ERROR_INTERNAL_NOT_INITIALIZED;
|
if (!parent) {
|
||||||
|
parent = &rootItem;
|
||||||
|
} else {
|
||||||
|
// We can only safely process items of a parent if the parent was the last
|
||||||
|
// item returned by WUPS_GetSubItem or WUPS_CreateSubItem
|
||||||
|
if (parent != sActiveSubItem) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY;
|
||||||
|
}
|
||||||
|
if (parent->type != WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return __internal_functions.get_item_size_function_ptr(__internal_functions.__storageroot_item, parent, key, itemType, outSize);
|
|
||||||
|
isDirty = true;
|
||||||
|
|
||||||
|
WUPSStorageError error;
|
||||||
|
wups_storage_item_t *item = addItem(parent, key, WUPS_STORAGE_TYPE_ITEM, &error);
|
||||||
|
if (item == nullptr) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
sActiveSubItem = item;
|
||||||
|
|
||||||
|
*outItem = item;
|
||||||
|
return WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSStorageError WUPS_GetSubItem(wups_storage_item_t *parent, const char *key, wups_storage_item_t **outItem) {
|
||||||
|
if (!storage_initialized) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpened) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key || outItem == nullptr) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
parent = &rootItem;
|
||||||
|
} else {
|
||||||
|
// We can only safely process items of a parent if the parent was the last
|
||||||
|
// item returned by WUPS_GetSubItem or WUPS_CreateSubItem
|
||||||
|
if (parent != sActiveSubItem) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY;
|
||||||
|
}
|
||||||
|
if (parent->type != WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < parent->data_size; i++) {
|
||||||
|
wups_storage_item_t *item = &((wups_storage_item_t *) parent->data)[i];
|
||||||
|
|
||||||
|
if (item->deleted || item->type != WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(item->key, key) == 0) {
|
||||||
|
sActiveSubItem = item;
|
||||||
|
*outItem = item;
|
||||||
|
return WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSStorageError WUPS_StoreString(wups_storage_item_t *parent, const char *key, const char *string) {
|
||||||
|
if (!storage_initialized) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpened) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key || !string) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
parent = &rootItem;
|
||||||
|
} else {
|
||||||
|
// We can only safely process items of a parent if the parent was the last
|
||||||
|
// item returned by WUPS_GetSubItem or WUPS_CreateSubItem
|
||||||
|
if (parent != sActiveSubItem) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY;
|
||||||
|
}
|
||||||
|
if (parent->type != WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirty = true;
|
||||||
|
|
||||||
|
WUPSStorageError error;
|
||||||
|
wups_storage_item_t *item = addItem(parent, key, WUPS_STORAGE_TYPE_STRING, &error);
|
||||||
|
if (item == nullptr) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t size = strlen(string) + 1;
|
||||||
|
item->data = malloc(size);
|
||||||
|
if (item->data == nullptr) {
|
||||||
|
item->key = nullptr;
|
||||||
|
item->deleted = true;
|
||||||
|
return WUPS_STORAGE_ERROR_MALLOC_FAILED;
|
||||||
|
}
|
||||||
|
item->data_size = size;
|
||||||
|
strcpy((char *) item->data, string);
|
||||||
|
|
||||||
|
return WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSStorageError WUPS_StoreBool(wups_storage_item_t *parent, const char *key, bool value) {
|
||||||
|
return WUPS_StoreInt(parent, key, (int32_t) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSStorageError WUPS_StoreInt(wups_storage_item_t *parent, const char *key, int32_t value) {
|
||||||
|
if (!storage_initialized) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpened) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
parent = &rootItem;
|
||||||
|
} else {
|
||||||
|
// We can only safely process items of a parent if the parent was the last
|
||||||
|
// item returned by WUPS_GetSubItem or WUPS_CreateSubItem
|
||||||
|
if (parent != sActiveSubItem) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY;
|
||||||
|
}
|
||||||
|
if (parent->type != WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirty = true;
|
||||||
|
|
||||||
|
WUPSStorageError error;
|
||||||
|
wups_storage_item_t *item = addItem(parent, key, WUPS_STORAGE_TYPE_INT, &error);
|
||||||
|
if (item == nullptr) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
item->data = malloc(sizeof(int32_t));
|
||||||
|
if (item->data == nullptr) {
|
||||||
|
item->key = nullptr;
|
||||||
|
item->deleted = true;
|
||||||
|
return WUPS_STORAGE_ERROR_MALLOC_FAILED;
|
||||||
|
}
|
||||||
|
item->data_size = sizeof(int32_t);
|
||||||
|
*(int32_t *) item->data = value;
|
||||||
|
|
||||||
|
return WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSStorageError WUPS_StoreBinary(wups_storage_item_t *parent, const char *key, const void *data, uint32_t size) {
|
||||||
|
if (!storage_initialized) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpened) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key || !data || size == 0) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
parent = &rootItem;
|
||||||
|
} else {
|
||||||
|
// We can only safely process items of a parent if the parent was the last
|
||||||
|
// item returned by WUPS_GetSubItem or WUPS_CreateSubItem
|
||||||
|
if (parent != sActiveSubItem) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY;
|
||||||
|
}
|
||||||
|
if (parent->type != WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirty = true;
|
||||||
|
|
||||||
|
WUPSStorageError error;
|
||||||
|
wups_storage_item_t *item = addItem(parent, key, WUPS_STORAGE_TYPE_STRING, &error);
|
||||||
|
if (item == nullptr) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
item->data = b64_encode((const uint8_t *) data, size);
|
||||||
|
if (item->data == nullptr) {
|
||||||
|
item->key = nullptr;
|
||||||
|
item->deleted = true;
|
||||||
|
return WUPS_STORAGE_ERROR_MALLOC_FAILED;
|
||||||
|
}
|
||||||
|
item->data_size = strlen((char *) data) + 1;
|
||||||
|
|
||||||
|
return WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSStorageError WUPS_GetString(wups_storage_item_t *parent, const char *key, char *outString, uint32_t maxSize) {
|
||||||
|
if (!storage_initialized) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpened) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key || !outString || maxSize == 0) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
parent = &rootItem;
|
||||||
|
} else {
|
||||||
|
// We can only safely process items of a parent if the parent was the last
|
||||||
|
// item returned by WUPS_GetSubItem or WUPS_CreateSubItem
|
||||||
|
if (parent != sActiveSubItem) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY;
|
||||||
|
}
|
||||||
|
if (parent->type != WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < parent->data_size; i++) {
|
||||||
|
wups_storage_item_t *item = &((wups_storage_item_t *) parent->data)[i];
|
||||||
|
|
||||||
|
if (item->deleted || item->type != WUPS_STORAGE_TYPE_STRING) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(item->key, key) == 0) {
|
||||||
|
strncpy(outString, (char *) item->data, maxSize);
|
||||||
|
return WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSStorageError WUPS_GetBool(wups_storage_item_t *parent, const char *key, bool *outBool) {
|
||||||
|
int32_t out;
|
||||||
|
WUPSStorageError result = WUPS_GetInt(parent, key, &out);
|
||||||
|
if (result != WUPS_STORAGE_ERROR_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outBool = out != 0;
|
||||||
|
|
||||||
|
return WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSStorageError WUPS_GetInt(wups_storage_item_t *parent, const char *key, int32_t *outInt) {
|
||||||
|
if (!storage_initialized) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpened) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key || !outInt) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
parent = &rootItem;
|
||||||
|
} else {
|
||||||
|
// We can only safely process items of a parent if the parent was the last
|
||||||
|
// item returned by WUPS_GetSubItem or WUPS_CreateSubItem
|
||||||
|
if (parent != sActiveSubItem) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY;
|
||||||
|
}
|
||||||
|
if (parent->type != WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < parent->data_size; i++) {
|
||||||
|
wups_storage_item_t *item = &((wups_storage_item_t *) parent->data)[i];
|
||||||
|
|
||||||
|
if (item->deleted || item->type != WUPS_STORAGE_TYPE_INT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(item->key, key) == 0) {
|
||||||
|
*outInt = *(int32_t *) item->data;
|
||||||
|
return WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSStorageError WUPS_GetBinary(wups_storage_item_t *parent, const char *key, void *outData, uint32_t maxSize) {
|
||||||
|
if (!storage_initialized) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpened) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key || !outData || maxSize == 0) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
parent = &rootItem;
|
||||||
|
} else {
|
||||||
|
// We can only safely process items of a parent if the parent was the last
|
||||||
|
// item returned by WUPS_GetSubItem or WUPS_CreateSubItem
|
||||||
|
if (parent != sActiveSubItem) {
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_ACTIVE_CATEGORY;
|
||||||
|
}
|
||||||
|
if (parent->type != WUPS_STORAGE_TYPE_ITEM) {
|
||||||
|
return WUPS_STORAGE_ERROR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < parent->data_size; i++) {
|
||||||
|
wups_storage_item_t *item = &((wups_storage_item_t *) parent->data)[i];
|
||||||
|
|
||||||
|
if (item->deleted || item->type != WUPS_STORAGE_TYPE_STRING) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(item->key, key) == 0) {
|
||||||
|
if (b64_decoded_size((char *) item->data) > maxSize) {
|
||||||
|
return WUPS_STORAGE_ERROR_BUFFER_TOO_SMALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b64_decode((char *) item->data, (uint8_t *) outData, item->data_size)) {
|
||||||
|
return WUPS_STORAGE_ERROR_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return WUPS_STORAGE_ERROR_B64_DECODE_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WUPS_STORAGE_ERROR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
@ -1,126 +0,0 @@
|
|||||||
#include <optional>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <wups/storage.h>
|
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI::DeleteItem(std::string_view key) noexcept {
|
|
||||||
WUPSStorageSubItem item(nullptr);
|
|
||||||
return item.DeleteItem(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI::SaveStorage(bool forceSave) {
|
|
||||||
return WUPSStorageAPI_SaveStorage(forceSave);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI::ForceReloadStorage() {
|
|
||||||
return WUPSStorageAPI_ForceReloadStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageAPI::WipeStorage() {
|
|
||||||
return WUPSStorageAPI_WipeStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<WUPSStorageSubItem> WUPSStorageAPI::CreateSubItem(std::string_view key, WUPSStorageError &err) noexcept {
|
|
||||||
WUPSStorageSubItem item(nullptr);
|
|
||||||
return item.CreateSubItem(key, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<WUPSStorageSubItem> WUPSStorageAPI::GetSubItem(std::string_view key, WUPSStorageError &err) noexcept {
|
|
||||||
WUPSStorageSubItem item(nullptr);
|
|
||||||
return item.GetSubItem(key, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<WUPSStorageSubItem> WUPSStorageAPI::GetOrCreateSubItem(std::string_view key, WUPSStorageError &err) noexcept {
|
|
||||||
WUPSStorageSubItem item(nullptr);
|
|
||||||
return item.GetOrCreateSubItem(key, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageSubItem WUPSStorageAPI::CreateSubItem(std::string_view key) {
|
|
||||||
WUPSStorageSubItem item(nullptr);
|
|
||||||
return item.CreateSubItem(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageSubItem WUPSStorageAPI::GetSubItem(std::string_view key) {
|
|
||||||
WUPSStorageSubItem item(nullptr);
|
|
||||||
return item.GetSubItem(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageSubItem WUPSStorageAPI::GetOrCreateSubItem(std::string_view key) {
|
|
||||||
WUPSStorageSubItem item(nullptr);
|
|
||||||
return item.GetOrCreateSubItem(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view WUPSStorageAPI::GetStatusStr(const WUPSStorageError &err) noexcept {
|
|
||||||
return WUPSStorageAPI_GetStatusStr(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageSubItem WUPSStorageAPI::GetRootItem() noexcept {
|
|
||||||
return WUPSStorageSubItem(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageError WUPSStorageSubItem::DeleteItem(std::string_view key) noexcept {
|
|
||||||
return WUPSStorageAPI_DeleteItem(mHandle, key.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<WUPSStorageSubItem> WUPSStorageSubItem::CreateSubItem(std::string_view key, WUPSStorageError &err) noexcept {
|
|
||||||
wups_storage_item outItem = {};
|
|
||||||
err = WUPSStorageAPI_CreateSubItem(mHandle, key.data(), &outItem);
|
|
||||||
if (err != WUPS_STORAGE_ERROR_SUCCESS) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return WUPSStorageSubItem(outItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<WUPSStorageSubItem> WUPSStorageSubItem::GetSubItem(std::string_view key, WUPSStorageError &err) const noexcept {
|
|
||||||
wups_storage_item outItem = {};
|
|
||||||
err = WUPSStorageAPI_GetSubItem(mHandle, key.data(), &outItem);
|
|
||||||
if (err != WUPS_STORAGE_ERROR_SUCCESS) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return WUPSStorageSubItem(outItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::optional<WUPSStorageSubItem> WUPSStorageSubItem::GetOrCreateSubItem(std::string_view key, WUPSStorageError &err) noexcept {
|
|
||||||
wups_storage_item outItem = {};
|
|
||||||
err = WUPSStorageAPI_GetSubItem(mHandle, key.data(), &outItem);
|
|
||||||
if (err == WUPS_STORAGE_ERROR_NOT_FOUND) {
|
|
||||||
return CreateSubItem(key, err);
|
|
||||||
}
|
|
||||||
return WUPSStorageSubItem(outItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageSubItem WUPSStorageSubItem::CreateSubItem(std::string_view key) {
|
|
||||||
WUPSStorageError err;
|
|
||||||
auto res = CreateSubItem(key, err);
|
|
||||||
if (!res) {
|
|
||||||
throw std::runtime_error(std::string("WUPSStorageSubItem::CreateSubItem(\"").append(key).append("\")").append(WUPSStorageAPI_GetStatusStr(err)));
|
|
||||||
}
|
|
||||||
return *res;
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageSubItem WUPSStorageSubItem::GetSubItem(std::string_view key) const {
|
|
||||||
WUPSStorageError err;
|
|
||||||
auto res = GetSubItem(key, err);
|
|
||||||
if (!res) {
|
|
||||||
throw std::runtime_error(std::string("WUPSStorageSubItem::GetSubItem(\"").append(key).append("\")").append(WUPSStorageAPI_GetStatusStr(err)));
|
|
||||||
}
|
|
||||||
return *res;
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageSubItem WUPSStorageSubItem::GetOrCreateSubItem(std::string_view key) {
|
|
||||||
WUPSStorageError err;
|
|
||||||
auto res = GetOrCreateSubItem(key, err);
|
|
||||||
if (!res) {
|
|
||||||
throw std::runtime_error(std::string("WUPSStorageSubItem::GetOrCreateSubItem(\"").append(key).append("\")").append(WUPSStorageAPI_GetStatusStr(err)));
|
|
||||||
}
|
|
||||||
return *res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WUPSStorageSubItem::operator==(const WUPSStorageSubItem &rhs) const {
|
|
||||||
return mHandle == rhs.mHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WUPSStorageSubItem::operator!=(const WUPSStorageSubItem &rhs) const {
|
|
||||||
return !(rhs == *this);
|
|
||||||
}
|
|
127
libraries/libwups/utils/base64.cpp
Normal file
127
libraries/libwups/utils/base64.cpp
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
#include "base64.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
size_t b64_encoded_size(size_t inlen) {
|
||||||
|
size_t ret;
|
||||||
|
|
||||||
|
ret = inlen;
|
||||||
|
if (inlen % 3 != 0)
|
||||||
|
ret += 3 - (inlen % 3);
|
||||||
|
ret /= 3;
|
||||||
|
ret *= 4;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *b64_encode(const uint8_t *in, size_t len) {
|
||||||
|
char *out;
|
||||||
|
size_t elen;
|
||||||
|
size_t i;
|
||||||
|
size_t j;
|
||||||
|
size_t v;
|
||||||
|
|
||||||
|
if (in == NULL || len == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
elen = b64_encoded_size(len);
|
||||||
|
out = (char *) malloc(elen + 1);
|
||||||
|
out[elen] = '\0';
|
||||||
|
|
||||||
|
for (i = 0, j = 0; i < len; i += 3, j += 4) {
|
||||||
|
v = in[i];
|
||||||
|
v = i + 1 < len ? v << 8 | in[i + 1] : v << 8;
|
||||||
|
v = i + 2 < len ? v << 8 | in[i + 2] : v << 8;
|
||||||
|
|
||||||
|
out[j] = b64chars[(v >> 18) & 0x3F];
|
||||||
|
out[j + 1] = b64chars[(v >> 12) & 0x3F];
|
||||||
|
if (i + 1 < len) {
|
||||||
|
out[j + 2] = b64chars[(v >> 6) & 0x3F];
|
||||||
|
} else {
|
||||||
|
out[j + 2] = '=';
|
||||||
|
}
|
||||||
|
if (i + 2 < len) {
|
||||||
|
out[j + 3] = b64chars[v & 0x3F];
|
||||||
|
} else {
|
||||||
|
out[j + 3] = '=';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t b64_decoded_size(const char *in) {
|
||||||
|
size_t len;
|
||||||
|
size_t ret;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (in == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len = strlen(in);
|
||||||
|
ret = len / 4 * 3;
|
||||||
|
|
||||||
|
for (i = len; i-- > 0;) {
|
||||||
|
if (in[i] == '=') {
|
||||||
|
ret--;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int b64invs[] = {
|
||||||
|
62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3,
|
||||||
|
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
||||||
|
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
|
||||||
|
|
||||||
|
static int b64_isvalidchar(char c) {
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return 1;
|
||||||
|
if (c >= 'A' && c <= 'Z')
|
||||||
|
return 1;
|
||||||
|
if (c >= 'a' && c <= 'z')
|
||||||
|
return 1;
|
||||||
|
if (c == '+' || c == '/' || c == '=')
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int b64_decode(const char *in, uint8_t *out, size_t outlen) {
|
||||||
|
size_t len;
|
||||||
|
size_t i;
|
||||||
|
size_t j;
|
||||||
|
int v;
|
||||||
|
|
||||||
|
if (in == NULL || out == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len = strlen(in);
|
||||||
|
if (outlen < b64_decoded_size(in) || len % 4 != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (!b64_isvalidchar(in[i])) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, j = 0; i < len; i += 4, j += 3) {
|
||||||
|
v = b64invs[in[i] - 43];
|
||||||
|
v = (v << 6) | b64invs[in[i + 1] - 43];
|
||||||
|
v = in[i + 2] == '=' ? v << 6 : (v << 6) | b64invs[in[i + 2] - 43];
|
||||||
|
v = in[i + 3] == '=' ? v << 6 : (v << 6) | b64invs[in[i + 3] - 43];
|
||||||
|
|
||||||
|
out[j] = (v >> 16) & 0xFF;
|
||||||
|
if (in[i + 2] != '=')
|
||||||
|
out[j + 1] = (v >> 8) & 0xFF;
|
||||||
|
if (in[i + 3] != '=')
|
||||||
|
out[j + 2] = v & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
20
libraries/libwups/utils/base64.h
Normal file
20
libraries/libwups/utils/base64.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// based on https://nachtimwald.com/2017/11/18/base64-encode-and-decode-in-c/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t b64_encoded_size(size_t inlen);
|
||||||
|
char *b64_encode(const uint8_t *in, size_t len);
|
||||||
|
|
||||||
|
size_t b64_decoded_size(const char *in);
|
||||||
|
int b64_decode(const char *in, uint8_t *out, size_t outlen);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -37,16 +37,6 @@ CXXFLAGS := $(CFLAGS)
|
|||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUPSSPECS)
|
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUPSSPECS)
|
||||||
|
|
||||||
ifeq ($(DEBUG),1)
|
|
||||||
CXXFLAGS += -DDEBUG -g
|
|
||||||
CFLAGS += -DDEBUG -g
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(DEBUG),VERBOSE)
|
|
||||||
CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
|
|
||||||
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
|
|
||||||
endif
|
|
||||||
|
|
||||||
LIBS := -lwups -lwut
|
LIBS := -lwups -lwut
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
@ -106,7 +96,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
|||||||
all: $(BUILD)
|
all: $(BUILD)
|
||||||
|
|
||||||
$(BUILD):
|
$(BUILD):
|
||||||
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
|
@[ -d $@ ] || mkdir -p $@
|
||||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
|
@ -1,234 +0,0 @@
|
|||||||
#include "utils/logger.h"
|
|
||||||
#include <coreinit/filesystem.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <wups.h>
|
|
||||||
#include <wups/config/WUPSConfigItemBoolean.h>
|
|
||||||
#include <wups/config/WUPSConfigItemMultipleValues.h>
|
|
||||||
#include <wups/config/WUPSConfigItemStub.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
Mandatory plugin information.
|
|
||||||
If not set correctly, the loader will refuse to use the plugin.
|
|
||||||
**/
|
|
||||||
WUPS_PLUGIN_NAME("Example plugin");
|
|
||||||
WUPS_PLUGIN_DESCRIPTION("This is just an example plugin and will log the FSOpenFile function.");
|
|
||||||
WUPS_PLUGIN_VERSION("v1.0");
|
|
||||||
WUPS_PLUGIN_AUTHOR("Maschell");
|
|
||||||
WUPS_PLUGIN_LICENSE("BSD");
|
|
||||||
|
|
||||||
#define LOG_FS_OPEN_CONFIG_ID "logFSOpen"
|
|
||||||
|
|
||||||
/**
|
|
||||||
All of this defines can be used in ANY file.
|
|
||||||
It's possible to split it up into multiple files.
|
|
||||||
|
|
||||||
**/
|
|
||||||
|
|
||||||
WUPS_USE_WUT_DEVOPTAB(); // Use the wut devoptabs
|
|
||||||
WUPS_USE_STORAGE("example_plugin"); // Unique id for the storage api
|
|
||||||
|
|
||||||
bool logFSOpen = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback that will be called if the config has been changed
|
|
||||||
*/
|
|
||||||
void logFSOpenChanged(ConfigItemBoolean *item, bool newValue) {
|
|
||||||
DEBUG_FUNCTION_LINE_INFO("New value in logFSOpenChanged: %d", newValue);
|
|
||||||
logFSOpen = newValue;
|
|
||||||
// If the value has changed, we store it in the storage.
|
|
||||||
WUPSStorageAPI_StoreBool(NULL, LOG_FS_OPEN_CONFIG_ID, logFSOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle root) {
|
|
||||||
{
|
|
||||||
// Let's create a new category called "Settings"
|
|
||||||
WUPSConfigCategoryHandle settingsCategory;
|
|
||||||
WUPSConfigAPICreateCategoryOptionsV1 settingsCategoryOptions = {.name = "Settings"};
|
|
||||||
if (WUPSConfigAPI_Category_Create(settingsCategoryOptions, &settingsCategory) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to create settings category");
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a new item to this settings category
|
|
||||||
if (WUPSConfigItemBoolean_AddToCategory(settingsCategory, LOG_FS_OPEN_CONFIG_ID, "Log FSOpen calls", true, logFSOpen, &logFSOpenChanged) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to add item to category");
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the category to the root.
|
|
||||||
if (WUPSConfigAPI_Category_AddCategory(root, settingsCategory) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to add category to root item");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// We can also have categories inside categories!
|
|
||||||
WUPSConfigCategoryHandle categoryLevel1;
|
|
||||||
WUPSConfigAPICreateCategoryOptionsV1 catLev1Options = {.name = "Category with subcategory"};
|
|
||||||
if (WUPSConfigAPI_Category_Create(catLev1Options, &categoryLevel1) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to create categoryLevel1");
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
WUPSConfigCategoryHandle categoryLevel2;
|
|
||||||
WUPSConfigAPICreateCategoryOptionsV1 catLev2Options = {.name = "Category inside category"};
|
|
||||||
if (WUPSConfigAPI_Category_Create(catLev2Options, &categoryLevel2) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to create categoryLevel1");
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
if (WUPSConfigItemBoolean_AddToCategory(categoryLevel2, "stubInsideCategory", "This is stub item inside a nested category", false, false, NULL) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to add stub item to root category");
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add categoryLevel2 to categoryLevel1
|
|
||||||
if (WUPSConfigAPI_Category_AddCategory(categoryLevel1, categoryLevel2) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to add category to root item");
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add categoryLevel2 to categoryLevel1
|
|
||||||
if (WUPSConfigAPI_Category_AddCategory(root, categoryLevel1) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to add category to root item");
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// We can also directly add items to the root category
|
|
||||||
if (WUPSConfigItemStub_AddToCategory(root, "This is stub item without category") != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to add stub item to root category");
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
ConfigItemMultipleValuesPair values[10];
|
|
||||||
int numOfElements = sizeof(values) / sizeof(values[0]);
|
|
||||||
for (int i = 0; i < numOfElements; i++) {
|
|
||||||
#define STR_SIZE 10
|
|
||||||
char *str = (char *) malloc(STR_SIZE);
|
|
||||||
if (!str) {
|
|
||||||
OSFatal("Failed to allocate memory");
|
|
||||||
}
|
|
||||||
snprintf(str, STR_SIZE, "%d", i);
|
|
||||||
values[i].value = i;
|
|
||||||
values[i].valueName = str;
|
|
||||||
}
|
|
||||||
WUPSConfigAPIStatus multValuesRes = WUPSConfigItemMultipleValues_AddToCategory(root, "multival", "Multiple values", 0, 0, values, numOfElements, NULL);
|
|
||||||
for (int i = 0; i < sizeof(values) / sizeof(values[0]); i++) {
|
|
||||||
free((void *) values[i].valueName);
|
|
||||||
}
|
|
||||||
if (multValuesRes != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigMenuClosedCallback() {
|
|
||||||
WUPSStorageAPI_SaveStorage(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Gets called ONCE when the plugin was loaded.
|
|
||||||
**/
|
|
||||||
INITIALIZE_PLUGIN() {
|
|
||||||
// Logging only works when compiled with `make DEBUG=1`. See the README for more information.
|
|
||||||
initLogging();
|
|
||||||
DEBUG_FUNCTION_LINE("INITIALIZE_PLUGIN of example_plugin!");
|
|
||||||
|
|
||||||
WUPSConfigAPIOptionsV1 configOptions = {.name = "example_plugin"};
|
|
||||||
if (WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to init config api");
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageError storageRes;
|
|
||||||
// Try to get value from storage
|
|
||||||
if ((storageRes = WUPSStorageAPI_GetBool(NULL, LOG_FS_OPEN_CONFIG_ID, &logFSOpen)) == WUPS_STORAGE_ERROR_NOT_FOUND) {
|
|
||||||
|
|
||||||
// Add the value to the storage if it's missing.
|
|
||||||
if (WUPSStorageAPI_StoreBool(NULL, LOG_FS_OPEN_CONFIG_ID, logFSOpen) != WUPS_STORAGE_ERROR_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to store bool");
|
|
||||||
}
|
|
||||||
} else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to get bool %s (%d)", WUPSConfigAPI_GetStatusStr(storageRes), storageRes);
|
|
||||||
} else {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Successfully read the value from storage: %d %s (%d)", logFSOpen, WUPSConfigAPI_GetStatusStr(storageRes), storageRes);
|
|
||||||
}
|
|
||||||
WUPSStorageAPI_SaveStorage(false);
|
|
||||||
|
|
||||||
deinitLogging();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Gets called when the plugin will be unloaded.
|
|
||||||
**/
|
|
||||||
DEINITIALIZE_PLUGIN() {
|
|
||||||
DEBUG_FUNCTION_LINE("DEINITIALIZE_PLUGIN of example_plugin!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Gets called when an application starts.
|
|
||||||
**/
|
|
||||||
ON_APPLICATION_START() {
|
|
||||||
initLogging();
|
|
||||||
|
|
||||||
DEBUG_FUNCTION_LINE("ON_APPLICATION_START of example_plugin!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets called when an application actually ends
|
|
||||||
*/
|
|
||||||
ON_APPLICATION_ENDS() {
|
|
||||||
deinitLogging();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Gets called when an application request to exit.
|
|
||||||
**/
|
|
||||||
ON_APPLICATION_REQUESTS_EXIT() {
|
|
||||||
DEBUG_FUNCTION_LINE_INFO("ON_APPLICATION_REQUESTS_EXIT of example_plugin!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
This defines a function replacement.
|
|
||||||
It allows to replace the system function with an own function.
|
|
||||||
So whenever a game / application calls an overridden function, your function gets called instead.
|
|
||||||
|
|
||||||
Currently it's only possible to override functions that are loaded from .rpl files of OSv10 (00050010-1000400A).
|
|
||||||
|
|
||||||
Signature of this macro:
|
|
||||||
DECL_FUNCTION( RETURN_TYPE, ARBITRARY_NAME_OF_FUNCTION , ARGS_SEPERATED_BY_COMMA){
|
|
||||||
//Your code goes here.
|
|
||||||
}
|
|
||||||
|
|
||||||
Within this macro, two more function get declare you can use.
|
|
||||||
my_ARBITRARY_NAME_OF_FUNCTION and real_ARBITRARY_NAME_OF_FUNCTION
|
|
||||||
|
|
||||||
RETURN_TYPE my_ARBITRARY_NAME_OF_FUNCTION(ARGS_SEPERATED_BY_COMMA):
|
|
||||||
is just name of the function that gets declared in this macro.
|
|
||||||
It has the same effect as calling the overridden function directly.
|
|
||||||
|
|
||||||
RETURN_TYPE real_ARBITRARY_NAME_OF_FUNCTION(ARGS_SEPERATED_BY_COMMA):
|
|
||||||
is the name of the function, that leads to function that was overridden.
|
|
||||||
Use this to call the original function that will be overridden.
|
|
||||||
CAUTION: Other plugins may already have manipulated the return value or arguments.
|
|
||||||
|
|
||||||
Use this macro for each function you want to override
|
|
||||||
**/
|
|
||||||
DECL_FUNCTION(int, FSOpenFile, FSClient *pClient, FSCmdBlock *pCmd, const char *path, const char *mode, int *handle, int error) {
|
|
||||||
int result = real_FSOpenFile(pClient, pCmd, path, mode, handle, error);
|
|
||||||
if (logFSOpen) {
|
|
||||||
DEBUG_FUNCTION_LINE_INFO("FSOpenFile called for folder %s! Result %d", path, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
This tells the loader which functions from which library (.rpl) should be replaced with which function from this file.
|
|
||||||
The list of possible libraries can be found in the wiki. (In general it's WUPS_LOADER_LIBRARY_ + the name of the RPL in caps lock)
|
|
||||||
|
|
||||||
WUPS_MUST_REPLACE(FUNCTION_NAME_IN_THIS_FILE, NAME_OF_LIB_WHICH_CONTAINS_THIS_FUNCTION, NAME_OF_FUNCTION_TO_OVERRIDE)
|
|
||||||
|
|
||||||
Define this for each function you want to override.
|
|
||||||
**/
|
|
||||||
WUPS_MUST_REPLACE(FSOpenFile, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile);
|
|
169
plugins/example_plugin/src/main.cpp
Normal file
169
plugins/example_plugin/src/main.cpp
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#include "utils/logger.h"
|
||||||
|
#include <coreinit/filesystem.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wups.h>
|
||||||
|
#include <wups/config/WUPSConfigItemBoolean.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
Mandatory plugin information.
|
||||||
|
If not set correctly, the loader will refuse to use the plugin.
|
||||||
|
**/
|
||||||
|
WUPS_PLUGIN_NAME("Example plugin");
|
||||||
|
WUPS_PLUGIN_DESCRIPTION("This is just an example plugin and will log the FSOpenFile function.");
|
||||||
|
WUPS_PLUGIN_VERSION("v1.0");
|
||||||
|
WUPS_PLUGIN_AUTHOR("Maschell");
|
||||||
|
WUPS_PLUGIN_LICENSE("BSD");
|
||||||
|
|
||||||
|
#define LOG_FS_OPEN_CONFIG_ID "logFSOpen"
|
||||||
|
|
||||||
|
/**
|
||||||
|
All of this defines can be used in ANY file.
|
||||||
|
It's possible to split it up into multiple files.
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
WUPS_USE_WUT_DEVOPTAB(); // Use the wut devoptabs
|
||||||
|
WUPS_USE_STORAGE("example_plugin"); // Unique id for the storage api
|
||||||
|
|
||||||
|
bool logFSOpen = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets called ONCE when the plugin was loaded.
|
||||||
|
**/
|
||||||
|
INITIALIZE_PLUGIN() {
|
||||||
|
// Logging only works when compiled with `make DEBUG=1`. See the README for more information.
|
||||||
|
initLogging();
|
||||||
|
DEBUG_FUNCTION_LINE("INITIALIZE_PLUGIN of example_plugin!");
|
||||||
|
|
||||||
|
// Open storage to read values
|
||||||
|
WUPSStorageError storageRes = WUPS_OpenStorage();
|
||||||
|
if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE("Failed to open storage %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes);
|
||||||
|
} else {
|
||||||
|
// Try to get value from storage
|
||||||
|
if ((storageRes = WUPS_GetBool(nullptr, LOG_FS_OPEN_CONFIG_ID, &logFSOpen)) == WUPS_STORAGE_ERROR_NOT_FOUND) {
|
||||||
|
// Add the value to the storage if it's missing.
|
||||||
|
if (WUPS_StoreBool(nullptr, LOG_FS_OPEN_CONFIG_ID, logFSOpen) != WUPS_STORAGE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE("Failed to store bool");
|
||||||
|
}
|
||||||
|
} else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE("Failed to get bool %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close storage
|
||||||
|
if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE("Failed to close storage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deinitLogging();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets called when the plugin will be unloaded.
|
||||||
|
**/
|
||||||
|
DEINITIALIZE_PLUGIN() {
|
||||||
|
DEBUG_FUNCTION_LINE("DEINITIALIZE_PLUGIN of example_plugin!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets called when an application starts.
|
||||||
|
**/
|
||||||
|
ON_APPLICATION_START() {
|
||||||
|
initLogging();
|
||||||
|
|
||||||
|
DEBUG_FUNCTION_LINE("ON_APPLICATION_START of example_plugin!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets called when an application actually ends
|
||||||
|
*/
|
||||||
|
ON_APPLICATION_ENDS() {
|
||||||
|
deinitLogging();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets called when an application request to exit.
|
||||||
|
**/
|
||||||
|
ON_APPLICATION_REQUESTS_EXIT() {
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("ON_APPLICATION_REQUESTS_EXIT of example_plugin!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that will be called if the config has been changed
|
||||||
|
*/
|
||||||
|
void logFSOpenChanged(ConfigItemBoolean *item, bool newValue) {
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("New value in logFSOpenChanged: %d", newValue);
|
||||||
|
logFSOpen = newValue;
|
||||||
|
// If the value has changed, we store it in the storage.
|
||||||
|
WUPS_StoreInt(nullptr, LOG_FS_OPEN_CONFIG_ID, logFSOpen);
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPS_GET_CONFIG() {
|
||||||
|
// We open the storage, so we can persist the configuration the user did.
|
||||||
|
if (WUPS_OpenStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE("Failed to open storage");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPSConfigHandle config;
|
||||||
|
WUPSConfig_CreateHandled(&config, "Example Plugin");
|
||||||
|
|
||||||
|
WUPSConfigCategoryHandle cat;
|
||||||
|
WUPSConfig_AddCategoryByNameHandled(config, "Logging", &cat);
|
||||||
|
|
||||||
|
WUPSConfigItemBoolean_AddToCategoryHandled(config, cat, LOG_FS_OPEN_CONFIG_ID, "Log FSOpen calls", logFSOpen, &logFSOpenChanged);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPS_CONFIG_CLOSED() {
|
||||||
|
// Save all changes
|
||||||
|
if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("Failed to close storage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This defines a function replacement.
|
||||||
|
It allows to replace the system function with an own function.
|
||||||
|
So whenever a game / application calls an overridden function, your function gets called instead.
|
||||||
|
|
||||||
|
Currently it's only possible to override functions that are loaded from .rpl files of OSv10 (00050010-1000400A).
|
||||||
|
|
||||||
|
Signature of this macro:
|
||||||
|
DECL_FUNCTION( RETURN_TYPE, ARBITRARY_NAME_OF_FUNCTION , ARGS_SEPERATED_BY_COMMA){
|
||||||
|
//Your code goes here.
|
||||||
|
}
|
||||||
|
|
||||||
|
Within this macro, two more function get declare you can use.
|
||||||
|
my_ARBITRARY_NAME_OF_FUNCTION and real_ARBITRARY_NAME_OF_FUNCTION
|
||||||
|
|
||||||
|
RETURN_TYPE my_ARBITRARY_NAME_OF_FUNCTION(ARGS_SEPERATED_BY_COMMA):
|
||||||
|
is just name of the function that gets declared in this macro.
|
||||||
|
It has the same effect as calling the overridden function directly.
|
||||||
|
|
||||||
|
RETURN_TYPE real_ARBITRARY_NAME_OF_FUNCTION(ARGS_SEPERATED_BY_COMMA):
|
||||||
|
is the name of the function, that leads to function that was overridden.
|
||||||
|
Use this to call the original function that will be overridden.
|
||||||
|
CAUTION: Other plugins may already have manipulated the return value or arguments.
|
||||||
|
|
||||||
|
Use this macro for each function you want to override
|
||||||
|
**/
|
||||||
|
DECL_FUNCTION(int, FSOpenFile, FSClient *pClient, FSCmdBlock *pCmd, const char *path, const char *mode, int *handle, int error) {
|
||||||
|
int result = real_FSOpenFile(pClient, pCmd, path, mode, handle, error);
|
||||||
|
if (logFSOpen) {
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("FSOpenFile called for folder %s! Result %d", path, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This tells the loader which functions from which library (.rpl) should be replaced with which function from this file.
|
||||||
|
The list of possible libraries can be found in the wiki. (In general it's WUPS_LOADER_LIBRARY_ + the name of the RPL in caps lock)
|
||||||
|
|
||||||
|
WUPS_MUST_REPLACE(FUNCTION_NAME_IN_THIS_FILE, NAME_OF_LIB_WHICH_CONTAINS_THIS_FUNCTION, NAME_OF_FUNCTION_TO_OVERRIDE)
|
||||||
|
|
||||||
|
Define this for each function you want to override.
|
||||||
|
**/
|
||||||
|
WUPS_MUST_REPLACE(FSOpenFile, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile);
|
@ -1,67 +0,0 @@
|
|||||||
# Generated from CLion C/C++ Code Style settings
|
|
||||||
BasedOnStyle: LLVM
|
|
||||||
AccessModifierOffset: -4
|
|
||||||
AlignAfterOpenBracket: Align
|
|
||||||
AlignConsecutiveAssignments: Consecutive
|
|
||||||
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
|
|
||||||
AlignOperands: Align
|
|
||||||
AllowAllArgumentsOnNextLine: false
|
|
||||||
AllowAllConstructorInitializersOnNextLine: false
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: false
|
|
||||||
AllowShortBlocksOnASingleLine: Always
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortFunctionsOnASingleLine: All
|
|
||||||
AllowShortIfStatementsOnASingleLine: Always
|
|
||||||
AllowShortLambdasOnASingleLine: All
|
|
||||||
AllowShortLoopsOnASingleLine: true
|
|
||||||
AlwaysBreakAfterReturnType: None
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
BraceWrapping:
|
|
||||||
AfterCaseLabel: false
|
|
||||||
AfterClass: false
|
|
||||||
AfterControlStatement: Never
|
|
||||||
AfterEnum: false
|
|
||||||
AfterFunction: false
|
|
||||||
AfterNamespace: false
|
|
||||||
AfterUnion: false
|
|
||||||
BeforeCatch: false
|
|
||||||
BeforeElse: false
|
|
||||||
IndentBraces: false
|
|
||||||
SplitEmptyFunction: false
|
|
||||||
SplitEmptyRecord: true
|
|
||||||
BreakBeforeBinaryOperators: None
|
|
||||||
BreakBeforeTernaryOperators: true
|
|
||||||
BreakConstructorInitializers: BeforeColon
|
|
||||||
BreakInheritanceList: BeforeColon
|
|
||||||
ColumnLimit: 0
|
|
||||||
CompactNamespaces: false
|
|
||||||
ContinuationIndentWidth: 8
|
|
||||||
IndentCaseLabels: true
|
|
||||||
IndentPPDirectives: None
|
|
||||||
IndentWidth: 4
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
|
||||||
MaxEmptyLinesToKeep: 2
|
|
||||||
NamespaceIndentation: All
|
|
||||||
ObjCSpaceAfterProperty: false
|
|
||||||
ObjCSpaceBeforeProtocolList: true
|
|
||||||
PointerAlignment: Right
|
|
||||||
ReflowComments: false
|
|
||||||
SpaceAfterCStyleCast: true
|
|
||||||
SpaceAfterLogicalNot: false
|
|
||||||
SpaceAfterTemplateKeyword: false
|
|
||||||
SpaceBeforeAssignmentOperators: true
|
|
||||||
SpaceBeforeCpp11BracedList: false
|
|
||||||
SpaceBeforeCtorInitializerColon: true
|
|
||||||
SpaceBeforeInheritanceColon: true
|
|
||||||
SpaceBeforeParens: ControlStatements
|
|
||||||
SpaceBeforeRangeBasedForLoopColon: true
|
|
||||||
SpaceInEmptyParentheses: false
|
|
||||||
SpacesBeforeTrailingComments: 1
|
|
||||||
SpacesInAngles: false
|
|
||||||
SpacesInCStyleCastParentheses: false
|
|
||||||
SpacesInContainerLiterals: false
|
|
||||||
SpacesInParentheses: false
|
|
||||||
SpacesInSquareBrackets: false
|
|
||||||
TabWidth: 4
|
|
||||||
UseTab: Never
|
|
@ -1,5 +0,0 @@
|
|||||||
FROM ghcr.io/wiiu-env/devkitppc:20230218
|
|
||||||
|
|
||||||
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20230215 /artifacts $DEVKITPRO
|
|
||||||
|
|
||||||
WORKDIR project
|
|
@ -1,135 +0,0 @@
|
|||||||
#-------------------------------------------------------------------------------
|
|
||||||
.SUFFIXES:
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
ifeq ($(strip $(DEVKITPRO)),)
|
|
||||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
|
||||||
endif
|
|
||||||
|
|
||||||
TOPDIR ?= $(CURDIR)
|
|
||||||
|
|
||||||
include $(DEVKITPRO)/wups/share/wups_rules
|
|
||||||
|
|
||||||
WUT_ROOT := $(DEVKITPRO)/wut
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# 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
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
TARGET := ExamplePluginCPP
|
|
||||||
BUILD := build
|
|
||||||
SOURCES := src src/utils
|
|
||||||
DATA := data
|
|
||||||
INCLUDES := src
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# options for code generation
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
|
||||||
$(MACHDEP)
|
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
|
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -std=c++20
|
|
||||||
|
|
||||||
ASFLAGS := -g $(ARCH)
|
|
||||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUPSSPECS)
|
|
||||||
|
|
||||||
LIBS := -lwups -lwut
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# list of directories containing libraries, this must be the top level
|
|
||||||
# containing include and lib
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
LIBDIRS := $(PORTLIBS) $(WUPS_ROOT) $(WUT_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)
|
|
||||||
|
|
||||||
.PHONY: $(BUILD) clean all
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
all: $(BUILD)
|
|
||||||
|
|
||||||
$(BUILD):
|
|
||||||
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
|
|
||||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
clean:
|
|
||||||
@echo clean ...
|
|
||||||
@rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
else
|
|
||||||
.PHONY: all
|
|
||||||
|
|
||||||
DEPENDS := $(OFILES:.o=.d)
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# main targets
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
all : $(OUTPUT).wps
|
|
||||||
|
|
||||||
$(OUTPUT).wps : $(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
|
|
||||||
#-------------------------------------------------------------------------------
|
|
@ -1,57 +0,0 @@
|
|||||||
# Example plugin
|
|
||||||
|
|
||||||
This is just a simple example plugin which can be used as a template.
|
|
||||||
The plugin logs the FSOpenFile calls via UDP (**Only when build via `make DEBUG=1`**).
|
|
||||||
|
|
||||||
The logging can be enabled/disabled via the WUPS Config menu (press L, DPAD Down and Minus on the GamePad, Pro Controller or Classic Controller).
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
(`[ENVIRONMENT]` is a placeholder for the actual environment name.)
|
|
||||||
|
|
||||||
1. Copy the file `ExamplePlugin.wps` into `sd:/wiiu/environments/[ENVIRONMENT]/plugins`.
|
|
||||||
2. Requires the [WiiUPluginLoaderBackend](https://github.com/wiiu-env/WiiUPluginLoaderBackend) in `sd:/wiiu/environments/[ENVIRONMENT]/modules`.
|
|
||||||
|
|
||||||
Start the environment (e.g Aroma) and the backend should load the plugin.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
For building you need:
|
|
||||||
|
|
||||||
- [wups](https://github.com/Maschell/WiiUPluginSystem)
|
|
||||||
- [wut](https://github.com/devkitpro/wut)
|
|
||||||
|
|
||||||
Install them (in this order) according to their README's. Don't forget the dependencies of the libs itself.
|
|
||||||
|
|
||||||
Then you should be able to compile via `make` (with no logging) or `make DEBUG=1` (with logging).
|
|
||||||
|
|
||||||
## Buildflags
|
|
||||||
|
|
||||||
### Logging
|
|
||||||
|
|
||||||
Building via `make` only logs errors (via OSReport). To enable logging via the [LoggingModule](https://github.com/wiiu-env/LoggingModule) set `DEBUG` to `1` or `VERBOSE`.
|
|
||||||
|
|
||||||
`make` Logs errors only (via OSReport).
|
|
||||||
`make DEBUG=1` Enables information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule).
|
|
||||||
`make DEBUG=VERBOSE` Enables verbose information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule).
|
|
||||||
|
|
||||||
If the [LoggingModule](https://github.com/wiiu-env/LoggingModule) is not present, it'll fallback to UDP (Port 4405) and [CafeOS](https://github.com/wiiu-env/USBSerialLoggingModule) logging.
|
|
||||||
|
|
||||||
## Building using the Dockerfile
|
|
||||||
|
|
||||||
It's possible to use a docker image for building. This way you don't need anything installed on your host system.
|
|
||||||
|
|
||||||
```
|
|
||||||
# Build docker image (only needed once)
|
|
||||||
docker build . -t example-plugin-builder
|
|
||||||
|
|
||||||
# make
|
|
||||||
docker run -it --rm -v ${PWD}:/project example-plugin-builder make DEBUG=1
|
|
||||||
|
|
||||||
# make clean
|
|
||||||
docker run -it --rm -v ${PWD}:/project example-plugin-builder make clean
|
|
||||||
```
|
|
||||||
|
|
||||||
## Format the code via docker
|
|
||||||
|
|
||||||
`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src -i`
|
|
@ -1,286 +0,0 @@
|
|||||||
#include "utils/logger.h"
|
|
||||||
#include <coreinit/filesystem.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <wups.h>
|
|
||||||
#include <wups/config/WUPSConfigItemBoolean.h>
|
|
||||||
#include <wups/config/WUPSConfigItemIntegerRange.h>
|
|
||||||
#include <wups/config/WUPSConfigItemMultipleValues.h>
|
|
||||||
#include <wups/config/WUPSConfigItemStub.h>
|
|
||||||
#include <wups/config_api.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
Mandatory plugin information.
|
|
||||||
If not set correctly, the loader will refuse to use the plugin.
|
|
||||||
**/
|
|
||||||
WUPS_PLUGIN_NAME("Example plugin C++");
|
|
||||||
WUPS_PLUGIN_DESCRIPTION("This is just an example plugin written in C++");
|
|
||||||
WUPS_PLUGIN_VERSION("v1.0");
|
|
||||||
WUPS_PLUGIN_AUTHOR("Maschell");
|
|
||||||
WUPS_PLUGIN_LICENSE("BSD");
|
|
||||||
|
|
||||||
#define LOG_FS_OPEN_CONFIG_ID "logFSOpen"
|
|
||||||
#define OTHER_EXAMPLE_BOOL_CONFIG_ID "otherBoolItem"
|
|
||||||
#define OTHER_EXAMPLE2_BOOL_CONFIG_ID "other2BoolItem"
|
|
||||||
#define INTEGER_RANGE_EXAMPLE_CONFIG_ID "intRangeExample"
|
|
||||||
#define MULTIPLE_VALUES_EXAMPLE_CONFIG_ID "multValueExample"
|
|
||||||
|
|
||||||
/**
|
|
||||||
All of this defines can be used in ANY file.
|
|
||||||
It's possible to split it up into multiple files.
|
|
||||||
|
|
||||||
**/
|
|
||||||
|
|
||||||
WUPS_USE_WUT_DEVOPTAB(); // Use the wut devoptabs
|
|
||||||
WUPS_USE_STORAGE("example_plugin_cpp"); // Unique id for the storage api
|
|
||||||
|
|
||||||
enum ExampleOptions {
|
|
||||||
EXAMPLE_OPTION_1 = 0,
|
|
||||||
EXAMPLE_OPTION_2 = 1,
|
|
||||||
EXAMPLE_OPTION_3 = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LOF_FS_OPEN_DEFAULT_VALUE true
|
|
||||||
#define INTEGER_RANGE_DEFAULT_VALUE 10
|
|
||||||
#define MULTIPLE_VALUES_DEFAULT_VALUE EXAMPLE_OPTION_2
|
|
||||||
|
|
||||||
bool sLogFSOpen = LOF_FS_OPEN_DEFAULT_VALUE;
|
|
||||||
int sIntegerRangeValue = INTEGER_RANGE_DEFAULT_VALUE;
|
|
||||||
ExampleOptions sExampleOptionValue = MULTIPLE_VALUES_DEFAULT_VALUE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback that will be called if the config has been changed
|
|
||||||
*/
|
|
||||||
void boolItemChanged(ConfigItemBoolean *item, bool newValue) {
|
|
||||||
DEBUG_FUNCTION_LINE_INFO("New value in boolItemChanged: %d", newValue);
|
|
||||||
if (std::string_view(LOG_FS_OPEN_CONFIG_ID) == item->identifier) {
|
|
||||||
sLogFSOpen = newValue;
|
|
||||||
// If the value has changed, we store it in the storage.
|
|
||||||
WUPSStorageAPI::Store(item->identifier, newValue);
|
|
||||||
} else if (std::string_view(OTHER_EXAMPLE_BOOL_CONFIG_ID) == item->identifier) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Other bool value has changed to %d", newValue);
|
|
||||||
} else if (std::string_view(OTHER_EXAMPLE2_BOOL_CONFIG_ID) == item->identifier) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Other2 bool value has changed to %d", newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void integerRangeItemChanged(ConfigItemIntegerRange *item, int newValue) {
|
|
||||||
DEBUG_FUNCTION_LINE_INFO("New value in integerRangeItemChanged: %d", newValue);
|
|
||||||
// If the value has changed, we store it in the storage.
|
|
||||||
if (std::string_view(LOG_FS_OPEN_CONFIG_ID) == item->identifier) {
|
|
||||||
sIntegerRangeValue = newValue;
|
|
||||||
// If the value has changed, we store it in the storage.
|
|
||||||
WUPSStorageAPI::Store(item->identifier, newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void multipleValueItemChanged(ConfigItemIntegerRange *item, uint32_t newValue) {
|
|
||||||
DEBUG_FUNCTION_LINE_INFO("New value in multipleValueItemChanged: %d", newValue);
|
|
||||||
// If the value has changed, we store it in the storage.
|
|
||||||
if (std::string_view(MULTIPLE_VALUES_EXAMPLE_CONFIG_ID) == item->identifier) {
|
|
||||||
sExampleOptionValue = (ExampleOptions) newValue;
|
|
||||||
// If the value has changed, we store it in the storage.
|
|
||||||
WUPSStorageAPI::Store(item->identifier, sExampleOptionValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) {
|
|
||||||
// To use the C++ API, we create new WUPSConfigCategory from the root handle!
|
|
||||||
WUPSConfigCategory root = WUPSConfigCategory(rootHandle);
|
|
||||||
|
|
||||||
// The functions of the Config API come in two variants: One that throws an exception, and another one which doesn't
|
|
||||||
// To use the Config API without exception see the example below this try/catch block.
|
|
||||||
try {
|
|
||||||
// Then we can simply create a new category
|
|
||||||
auto functionPatchesCat = WUPSConfigCategory::Create("function patches");
|
|
||||||
|
|
||||||
// Add a boolean item to this newly created category
|
|
||||||
functionPatchesCat.add(WUPSConfigItemBoolean::Create(LOG_FS_OPEN_CONFIG_ID, "Log FSOpen calls",
|
|
||||||
LOF_FS_OPEN_DEFAULT_VALUE, sLogFSOpen,
|
|
||||||
boolItemChanged));
|
|
||||||
|
|
||||||
// And finally move that category to the root category.
|
|
||||||
// Note: "functionPatchesCat" can NOT be changed after adding it to root.
|
|
||||||
root.add(std::move(functionPatchesCat));
|
|
||||||
|
|
||||||
// We can also add items directly to root!
|
|
||||||
root.add(WUPSConfigItemBoolean::Create(OTHER_EXAMPLE_BOOL_CONFIG_ID, "Just another bool item",
|
|
||||||
false, false,
|
|
||||||
boolItemChanged));
|
|
||||||
|
|
||||||
// You can also add an item which just displays any text.
|
|
||||||
root.add(WUPSConfigItemStub::Create("This item is just displaying some text"));
|
|
||||||
|
|
||||||
// It's also possible to create and item to select an integer from a range.
|
|
||||||
root.add(WUPSConfigItemIntegerRange::Create(INTEGER_RANGE_EXAMPLE_CONFIG_ID, "Item for selecting an integer between 0 and 50",
|
|
||||||
INTEGER_RANGE_DEFAULT_VALUE, sIntegerRangeValue,
|
|
||||||
0, 50,
|
|
||||||
&integerRangeItemChanged));
|
|
||||||
|
|
||||||
|
|
||||||
// To select value from an enum WUPSConfigItemMultipleValues fits the best.
|
|
||||||
constexpr WUPSConfigItemMultipleValues::ValuePair possibleValues[] = {
|
|
||||||
{EXAMPLE_OPTION_1, "Option 1"},
|
|
||||||
{EXAMPLE_OPTION_2, "Option 2"},
|
|
||||||
{EXAMPLE_OPTION_3, "Option 3"},
|
|
||||||
};
|
|
||||||
|
|
||||||
// It comes in two variants.
|
|
||||||
// - "WUPSConfigItemMultipleValues::CreateFromValue" will take a default and current **value**
|
|
||||||
// - "WUPSConfigItemMultipleValues::CreateFromIndex" will take a default and current **index**
|
|
||||||
root.add(WUPSConfigItemMultipleValues::CreateFromValue(MULTIPLE_VALUES_EXAMPLE_CONFIG_ID, "Select an option!",
|
|
||||||
MULTIPLE_VALUES_DEFAULT_VALUE, sExampleOptionValue,
|
|
||||||
possibleValues,
|
|
||||||
nullptr));
|
|
||||||
|
|
||||||
// It's also possible to have nested categories
|
|
||||||
auto nc1 = WUPSConfigCategory::Create("Category inside root");
|
|
||||||
auto nc2 = WUPSConfigCategory::Create("Category inside subcategory 1");
|
|
||||||
auto nc3 = WUPSConfigCategory::Create("Category inside subcategory 2");
|
|
||||||
|
|
||||||
nc3.add(WUPSConfigItemStub::Create("Item inside subcategory 3"));
|
|
||||||
nc2.add(WUPSConfigItemStub::Create("Item inside subcategory 2"));
|
|
||||||
nc1.add(WUPSConfigItemStub::Create("Item inside subcategory 1"));
|
|
||||||
|
|
||||||
nc2.add(std::move(nc3));
|
|
||||||
nc1.add(std::move(nc2));
|
|
||||||
root.add(std::move(nc1));
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Creating config menu failed: %s", e.what());
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case we don't like exception, we can use the API as well.
|
|
||||||
// If we add a "WUPSConfigAPIStatus" reference to the API calls, the function won't throw an exception.
|
|
||||||
// Instead it will return std::optionals and write the result into the WUPSConfigAPIStatus.
|
|
||||||
WUPSConfigAPIStatus err;
|
|
||||||
auto categoryOpt = WUPSConfigCategory::Create("Just another Category", err);
|
|
||||||
if (!categoryOpt) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to create category: %s", WUPSConfigAPI_GetStatusStr(err));
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto boolItemOpt = WUPSConfigItemBoolean::Create(OTHER_EXAMPLE2_BOOL_CONFIG_ID, "Just another bool item",
|
|
||||||
false, false,
|
|
||||||
boolItemChanged,
|
|
||||||
err);
|
|
||||||
if (!boolItemOpt) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to create bool item: %s", WUPSConfigAPI_GetStatusStr(err));
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add bool item to category
|
|
||||||
if (!categoryOpt->add(std::move(*boolItemOpt), err)) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to add bool item to category: %s", WUPSConfigAPI_GetStatusStr(err));
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add category to root.
|
|
||||||
if (!root.add(std::move(*categoryOpt), err)) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to add category to root: %s", WUPSConfigAPI_GetStatusStr(err));
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigMenuClosedCallback() {
|
|
||||||
WUPSStorageAPI::SaveStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Gets called ONCE when the plugin was loaded.
|
|
||||||
**/
|
|
||||||
INITIALIZE_PLUGIN() {
|
|
||||||
// Logging only works when compiled with `make DEBUG=1`. See the README for more information.
|
|
||||||
initLogging();
|
|
||||||
DEBUG_FUNCTION_LINE("INITIALIZE_PLUGIN of example_plugin!");
|
|
||||||
|
|
||||||
WUPSConfigAPIOptionsV1 configOptions = {.name = "example_plugin_cpp"};
|
|
||||||
if (WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) != WUPSCONFIG_API_RESULT_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to init config api");
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPSStorageError storageRes;
|
|
||||||
if ((storageRes = WUPSStorageAPI::GetOrStoreDefault(LOG_FS_OPEN_CONFIG_ID, sLogFSOpen, LOF_FS_OPEN_DEFAULT_VALUE)) != WUPS_STORAGE_ERROR_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("GetOrStoreDefault failed: %s (%d)", WUPSStorageAPI_GetStatusStr(storageRes), storageRes);
|
|
||||||
}
|
|
||||||
if ((storageRes = WUPSStorageAPI::SaveStorage()) != WUPS_STORAGE_ERROR_SUCCESS) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("GetOrStoreDefault failed: %s (%d)", WUPSStorageAPI_GetStatusStr(storageRes), storageRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
deinitLogging();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Gets called when the plugin will be unloaded.
|
|
||||||
**/
|
|
||||||
DEINITIALIZE_PLUGIN() {
|
|
||||||
DEBUG_FUNCTION_LINE("DEINITIALIZE_PLUGIN of example_plugin!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Gets called when an application starts.
|
|
||||||
**/
|
|
||||||
ON_APPLICATION_START() {
|
|
||||||
initLogging();
|
|
||||||
|
|
||||||
DEBUG_FUNCTION_LINE("ON_APPLICATION_START of example_plugin!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets called when an application actually ends
|
|
||||||
*/
|
|
||||||
ON_APPLICATION_ENDS() {
|
|
||||||
deinitLogging();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Gets called when an application request to exit.
|
|
||||||
**/
|
|
||||||
ON_APPLICATION_REQUESTS_EXIT() {
|
|
||||||
DEBUG_FUNCTION_LINE_INFO("ON_APPLICATION_REQUESTS_EXIT of example_plugin!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
This defines a function replacement.
|
|
||||||
It allows to replace the system function with an own function.
|
|
||||||
So whenever a game / application calls an overridden function, your function gets called instead.
|
|
||||||
|
|
||||||
Currently it's only possible to override functions that are loaded from .rpl files of OSv10 (00050010-1000400A).
|
|
||||||
|
|
||||||
Signature of this macro:
|
|
||||||
DECL_FUNCTION( RETURN_TYPE, ARBITRARY_NAME_OF_FUNCTION , ARGS_SEPERATED_BY_COMMA){
|
|
||||||
//Your code goes here.
|
|
||||||
}
|
|
||||||
|
|
||||||
Within this macro, two more function get declare you can use.
|
|
||||||
my_ARBITRARY_NAME_OF_FUNCTION and real_ARBITRARY_NAME_OF_FUNCTION
|
|
||||||
|
|
||||||
RETURN_TYPE my_ARBITRARY_NAME_OF_FUNCTION(ARGS_SEPERATED_BY_COMMA):
|
|
||||||
is just name of the function that gets declared in this macro.
|
|
||||||
It has the same effect as calling the overridden function directly.
|
|
||||||
|
|
||||||
RETURN_TYPE real_ARBITRARY_NAME_OF_FUNCTION(ARGS_SEPERATED_BY_COMMA):
|
|
||||||
is the name of the function, that leads to function that was overridden.
|
|
||||||
Use this to call the original function that will be overridden.
|
|
||||||
CAUTION: Other plugins may already have manipulated the return value or arguments.
|
|
||||||
|
|
||||||
Use this macro for each function you want to override
|
|
||||||
**/
|
|
||||||
DECL_FUNCTION(int, FSOpenFile, FSClient *pClient, FSCmdBlock *pCmd, const char *path, const char *mode, int *handle, int error) {
|
|
||||||
int result = real_FSOpenFile(pClient, pCmd, path, mode, handle, error);
|
|
||||||
if (sLogFSOpen) {
|
|
||||||
DEBUG_FUNCTION_LINE_INFO("FSOpenFile called for folder %s! Result %d", path, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
This tells the loader which functions from which library (.rpl) should be replaced with which function from this file.
|
|
||||||
The list of possible libraries can be found in the wiki. (In general it's WUPS_LOADER_LIBRARY_ + the name of the RPL in caps lock)
|
|
||||||
|
|
||||||
WUPS_MUST_REPLACE(FUNCTION_NAME_IN_THIS_FILE, NAME_OF_LIB_WHICH_CONTAINS_THIS_FUNCTION, NAME_OF_FUNCTION_TO_OVERRIDE)
|
|
||||||
|
|
||||||
Define this for each function you want to override.
|
|
||||||
**/
|
|
||||||
WUPS_MUST_REPLACE(FSOpenFile, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile);
|
|
@ -1,36 +0,0 @@
|
|||||||
#ifdef DEBUG
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <whb/log_cafe.h>
|
|
||||||
#include <whb/log_module.h>
|
|
||||||
#include <whb/log_udp.h>
|
|
||||||
|
|
||||||
uint32_t moduleLogInit = false;
|
|
||||||
uint32_t cafeLogInit = false;
|
|
||||||
uint32_t udpLogInit = false;
|
|
||||||
#endif // DEBUG
|
|
||||||
|
|
||||||
void initLogging() {
|
|
||||||
#ifdef DEBUG
|
|
||||||
if (!(moduleLogInit = WHBLogModuleInit())) {
|
|
||||||
cafeLogInit = WHBLogCafeInit();
|
|
||||||
udpLogInit = WHBLogUdpInit();
|
|
||||||
}
|
|
||||||
#endif // DEBUG
|
|
||||||
}
|
|
||||||
|
|
||||||
void deinitLogging() {
|
|
||||||
#ifdef DEBUG
|
|
||||||
if (moduleLogInit) {
|
|
||||||
WHBLogModuleDeinit();
|
|
||||||
moduleLogInit = false;
|
|
||||||
}
|
|
||||||
if (cafeLogInit) {
|
|
||||||
WHBLogCafeDeinit();
|
|
||||||
cafeLogInit = false;
|
|
||||||
}
|
|
||||||
if (udpLogInit) {
|
|
||||||
WHBLogUdpDeinit();
|
|
||||||
udpLogInit = false;
|
|
||||||
}
|
|
||||||
#endif // DEBUG
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <coreinit/debug.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <whb/log.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LOG_APP_TYPE "P"
|
|
||||||
#define LOG_APP_NAME "ExamplePlugin"
|
|
||||||
|
|
||||||
#define __FILENAME__ ({ \
|
|
||||||
const char *__filename = __FILE__; \
|
|
||||||
const char *__pos = strrchr(__filename, '/'); \
|
|
||||||
if (!__pos) __pos = strrchr(__filename, '\\'); \
|
|
||||||
__pos ? __pos + 1 : __filename; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define LOG(LOG_FUNC, FMT, ARGS...) LOG_EX_DEFAULT(LOG_FUNC, "", "", FMT, ##ARGS)
|
|
||||||
|
|
||||||
#define LOG_EX_DEFAULT(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ##ARGS)
|
|
||||||
|
|
||||||
#define LOG_EX(FILENAME, FUNCTION, LINE, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) \
|
|
||||||
do { \
|
|
||||||
LOG_FUNC("[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, FILENAME, FUNCTION, LINE, ##ARGS); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
|
|
||||||
#ifdef VERBOSE_DEBUG
|
|
||||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS)
|
|
||||||
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "", "", FMT, ##ARGS);
|
|
||||||
#else
|
|
||||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
|
|
||||||
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS)
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS)
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
|
|
||||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS)
|
|
||||||
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##INFO ## ", "", FMT, ##ARGS)
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS);
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0)
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0)
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
|
|
||||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARN ## ", "\n", FMT, ##ARGS)
|
|
||||||
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##INFO ## ", "\n", FMT, ##ARGS)
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void initLogging();
|
|
||||||
|
|
||||||
void deinitLogging();
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,67 +0,0 @@
|
|||||||
# Generated from CLion C/C++ Code Style settings
|
|
||||||
BasedOnStyle: LLVM
|
|
||||||
AccessModifierOffset: -4
|
|
||||||
AlignAfterOpenBracket: Align
|
|
||||||
AlignConsecutiveAssignments: Consecutive
|
|
||||||
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
|
|
||||||
AlignOperands: Align
|
|
||||||
AllowAllArgumentsOnNextLine: false
|
|
||||||
AllowAllConstructorInitializersOnNextLine: false
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: false
|
|
||||||
AllowShortBlocksOnASingleLine: Always
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortFunctionsOnASingleLine: All
|
|
||||||
AllowShortIfStatementsOnASingleLine: Always
|
|
||||||
AllowShortLambdasOnASingleLine: All
|
|
||||||
AllowShortLoopsOnASingleLine: true
|
|
||||||
AlwaysBreakAfterReturnType: None
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
BraceWrapping:
|
|
||||||
AfterCaseLabel: false
|
|
||||||
AfterClass: false
|
|
||||||
AfterControlStatement: Never
|
|
||||||
AfterEnum: false
|
|
||||||
AfterFunction: false
|
|
||||||
AfterNamespace: false
|
|
||||||
AfterUnion: false
|
|
||||||
BeforeCatch: false
|
|
||||||
BeforeElse: false
|
|
||||||
IndentBraces: false
|
|
||||||
SplitEmptyFunction: false
|
|
||||||
SplitEmptyRecord: true
|
|
||||||
BreakBeforeBinaryOperators: None
|
|
||||||
BreakBeforeTernaryOperators: true
|
|
||||||
BreakConstructorInitializers: BeforeColon
|
|
||||||
BreakInheritanceList: BeforeColon
|
|
||||||
ColumnLimit: 0
|
|
||||||
CompactNamespaces: false
|
|
||||||
ContinuationIndentWidth: 8
|
|
||||||
IndentCaseLabels: true
|
|
||||||
IndentPPDirectives: None
|
|
||||||
IndentWidth: 4
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
|
||||||
MaxEmptyLinesToKeep: 2
|
|
||||||
NamespaceIndentation: All
|
|
||||||
ObjCSpaceAfterProperty: false
|
|
||||||
ObjCSpaceBeforeProtocolList: true
|
|
||||||
PointerAlignment: Right
|
|
||||||
ReflowComments: false
|
|
||||||
SpaceAfterCStyleCast: true
|
|
||||||
SpaceAfterLogicalNot: false
|
|
||||||
SpaceAfterTemplateKeyword: false
|
|
||||||
SpaceBeforeAssignmentOperators: true
|
|
||||||
SpaceBeforeCpp11BracedList: false
|
|
||||||
SpaceBeforeCtorInitializerColon: true
|
|
||||||
SpaceBeforeInheritanceColon: true
|
|
||||||
SpaceBeforeParens: ControlStatements
|
|
||||||
SpaceBeforeRangeBasedForLoopColon: true
|
|
||||||
SpaceInEmptyParentheses: false
|
|
||||||
SpacesBeforeTrailingComments: 1
|
|
||||||
SpacesInAngles: false
|
|
||||||
SpacesInCStyleCastParentheses: false
|
|
||||||
SpacesInContainerLiterals: false
|
|
||||||
SpacesInParentheses: false
|
|
||||||
SpacesInSquareBrackets: false
|
|
||||||
TabWidth: 4
|
|
||||||
UseTab: Never
|
|
@ -1,5 +0,0 @@
|
|||||||
FROM ghcr.io/wiiu-env/devkitppc:20230218
|
|
||||||
|
|
||||||
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20230215 /artifacts $DEVKITPRO
|
|
||||||
|
|
||||||
WORKDIR project
|
|
@ -1,143 +0,0 @@
|
|||||||
#-------------------------------------------------------------------------------
|
|
||||||
.SUFFIXES:
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
ifeq ($(strip $(DEVKITPRO)),)
|
|
||||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
|
||||||
endif
|
|
||||||
|
|
||||||
TOPDIR ?= $(CURDIR)
|
|
||||||
|
|
||||||
include $(DEVKITPRO)/wups/share/wups_rules
|
|
||||||
|
|
||||||
WUT_ROOT := $(DEVKITPRO)/wut
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# 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
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
TARGET := StorageTestPlugin
|
|
||||||
BUILD := build
|
|
||||||
SOURCES := src src/utils \
|
|
||||||
src/catch2 \
|
|
||||||
src/catch2/benchmark \
|
|
||||||
src/catch2/generators \
|
|
||||||
src/catch2/interfaces \
|
|
||||||
src/catch2/internal \
|
|
||||||
src/catch2/matchers \
|
|
||||||
src/catch2/matchers/internal \
|
|
||||||
src/catch2/reporters \
|
|
||||||
DATA := data
|
|
||||||
INCLUDES := src
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# options for code generation
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
|
||||||
$(MACHDEP)
|
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
|
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS)
|
|
||||||
|
|
||||||
ASFLAGS := -g $(ARCH)
|
|
||||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUPSSPECS)
|
|
||||||
|
|
||||||
LIBS := -lwups -lwut
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# list of directories containing libraries, this must be the top level
|
|
||||||
# containing include and lib
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
LIBDIRS := $(PORTLIBS) $(WUPS_ROOT) $(WUT_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)
|
|
||||||
|
|
||||||
.PHONY: $(BUILD) clean all
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
all: $(BUILD)
|
|
||||||
|
|
||||||
$(BUILD):
|
|
||||||
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
|
|
||||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
clean:
|
|
||||||
@echo clean ...
|
|
||||||
@rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
else
|
|
||||||
.PHONY: all
|
|
||||||
|
|
||||||
DEPENDS := $(OFILES:.o=.d)
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# main targets
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
all : $(OUTPUT).wps
|
|
||||||
|
|
||||||
$(OUTPUT).wps : $(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
|
|
||||||
#-------------------------------------------------------------------------------
|
|
@ -1,55 +0,0 @@
|
|||||||
# Storage test plugin
|
|
||||||
|
|
||||||
This plugin implements several tests of StorageAPI. Results are be printed to the OSConsole ([USBSerialLoggingModule](https://github.com/wiiu-env/USBSerialLoggingModule))
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
(`[ENVIRONMENT]` is a placeholder for the actual environment name.)
|
|
||||||
|
|
||||||
1. Requires the [WiiUPluginLoaderBackend](https://github.com/wiiu-env/WiiUPluginLoaderBackend) in `sd:/wiiu/environments/[ENVIRONMENT]/modules`.
|
|
||||||
|
|
||||||
It's recommended to [wiiload](https://github.com/wiiu-env/wiiload_plugin) this plugin whenever you want to run the test.
|
|
||||||
|
|
||||||
This plugin uses [Catch2](https://github.com/catchorg/Catch2) to run tests.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
For building you need:
|
|
||||||
|
|
||||||
- [wups](https://github.com/Maschell/WiiUPluginSystem)
|
|
||||||
- [wut](https://github.com/devkitpro/wut)
|
|
||||||
|
|
||||||
Install them (in this order) according to their README's. Don't forget the dependencies of the libs itself.
|
|
||||||
|
|
||||||
Then you should be able to compile via `make` (with no logging) or `make DEBUG=1` (with logging).
|
|
||||||
|
|
||||||
## Buildflags
|
|
||||||
|
|
||||||
### Logging
|
|
||||||
|
|
||||||
Building via `make` only logs errors (via OSReport). To enable logging via the [LoggingModule](https://github.com/wiiu-env/LoggingModule) set `DEBUG` to `1` or `VERBOSE`.
|
|
||||||
|
|
||||||
`make` Logs errors only (via OSReport).
|
|
||||||
`make DEBUG=1` Enables information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule).
|
|
||||||
`make DEBUG=VERBOSE` Enables verbose information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule).
|
|
||||||
|
|
||||||
If the [LoggingModule](https://github.com/wiiu-env/LoggingModule) isn't present, it will fall back to UDP (port 4405) and [CafeOS](https://github.com/wiiu-env/USBSerialLoggingModule) logging.
|
|
||||||
|
|
||||||
## Building using the Dockerfile
|
|
||||||
|
|
||||||
It's possible to use a docker image for building. This way you don't need anything installed on your host system.
|
|
||||||
|
|
||||||
```
|
|
||||||
# Build docker image (only needed once)
|
|
||||||
docker build . -t storage-test-plugin-builder
|
|
||||||
|
|
||||||
# make
|
|
||||||
docker run -it --rm -v ${PWD}:/project storage-test-plugin-builder make DEBUG=1
|
|
||||||
|
|
||||||
# make clean
|
|
||||||
docker run -it --rm -v ${PWD}:/project storage-test-plugin-builder make clean
|
|
||||||
```
|
|
||||||
|
|
||||||
## Format the code via docker
|
|
||||||
|
|
||||||
`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src --exclude ./src/catch2 -i`
|
|
@ -1,148 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_BENCHMARK_HPP_INCLUDED
|
|
||||||
#define CATCH_BENCHMARK_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/catch_user_config.hpp>
|
|
||||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
|
||||||
#include <catch2/internal/catch_context.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
#include <catch2/internal/catch_test_failure_exception.hpp>
|
|
||||||
#include <catch2/internal/catch_unique_name.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
|
|
||||||
#include <catch2/benchmark/catch_clock.hpp>
|
|
||||||
#include <catch2/benchmark/catch_environment.hpp>
|
|
||||||
#include <catch2/benchmark/catch_execution_plan.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_estimate_clock.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_analyse.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <chrono>
|
|
||||||
#include <exception>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
struct Benchmark {
|
|
||||||
Benchmark(std::string&& benchmarkName)
|
|
||||||
: name(CATCH_MOVE(benchmarkName)) {}
|
|
||||||
|
|
||||||
template <class FUN>
|
|
||||||
Benchmark(std::string&& benchmarkName , FUN &&func)
|
|
||||||
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
|
|
||||||
|
|
||||||
template <typename Clock>
|
|
||||||
ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
|
|
||||||
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
|
|
||||||
auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
|
|
||||||
auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun);
|
|
||||||
int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
|
|
||||||
return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Clock = default_clock>
|
|
||||||
void run() {
|
|
||||||
auto const* cfg = getCurrentContext().getConfig();
|
|
||||||
|
|
||||||
auto env = Detail::measure_environment<Clock>();
|
|
||||||
|
|
||||||
getResultCapture().benchmarkPreparing(name);
|
|
||||||
CATCH_TRY{
|
|
||||||
auto plan = user_code([&] {
|
|
||||||
return prepare<Clock>(*cfg, env);
|
|
||||||
});
|
|
||||||
|
|
||||||
BenchmarkInfo info {
|
|
||||||
CATCH_MOVE(name),
|
|
||||||
plan.estimated_duration.count(),
|
|
||||||
plan.iterations_per_sample,
|
|
||||||
cfg->benchmarkSamples(),
|
|
||||||
cfg->benchmarkResamples(),
|
|
||||||
env.clock_resolution.mean.count(),
|
|
||||||
env.clock_cost.mean.count()
|
|
||||||
};
|
|
||||||
|
|
||||||
getResultCapture().benchmarkStarting(info);
|
|
||||||
|
|
||||||
auto samples = user_code([&] {
|
|
||||||
return plan.template run<Clock>(*cfg, env);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
|
|
||||||
BenchmarkStats<FloatDuration<Clock>> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
|
|
||||||
getResultCapture().benchmarkEnded(stats);
|
|
||||||
} CATCH_CATCH_ANON (TestFailureException const&) {
|
|
||||||
getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
|
|
||||||
} CATCH_CATCH_ALL{
|
|
||||||
getResultCapture().benchmarkFailed(translateActiveException());
|
|
||||||
// We let the exception go further up so that the
|
|
||||||
// test case is marked as failed.
|
|
||||||
std::rethrow_exception(std::current_exception());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sets lambda to be used in fun *and* executes benchmark!
|
|
||||||
template <typename Fun, std::enable_if_t<!Detail::is_related<Fun, Benchmark>::value, int> = 0>
|
|
||||||
Benchmark & operator=(Fun func) {
|
|
||||||
auto const* cfg = getCurrentContext().getConfig();
|
|
||||||
if (!cfg->skipBenchmarks()) {
|
|
||||||
fun = Detail::BenchmarkFunction(func);
|
|
||||||
run();
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Detail::BenchmarkFunction fun;
|
|
||||||
std::string name;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1
|
|
||||||
#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2
|
|
||||||
|
|
||||||
#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\
|
|
||||||
if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
|
|
||||||
BenchmarkName = [&](int benchmarkIndex)
|
|
||||||
|
|
||||||
#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\
|
|
||||||
if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
|
|
||||||
BenchmarkName = [&]
|
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_PREFIX_ALL)
|
|
||||||
|
|
||||||
#define CATCH_BENCHMARK(...) \
|
|
||||||
INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
|
|
||||||
#define CATCH_BENCHMARK_ADVANCED(name) \
|
|
||||||
INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name)
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define BENCHMARK(...) \
|
|
||||||
INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
|
|
||||||
#define BENCHMARK_ADVANCED(name) \
|
|
||||||
INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // CATCH_BENCHMARK_HPP_INCLUDED
|
|
@ -1,46 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
/** \file
|
|
||||||
* This is a convenience header for Catch2's benchmarking. It includes
|
|
||||||
* **all** of Catch2 headers related to benchmarking.
|
|
||||||
*
|
|
||||||
* Generally the Catch2 users should use specific includes they need,
|
|
||||||
* but this header can be used instead for ease-of-experimentation, or
|
|
||||||
* just plain convenience, at the cost of (significantly) increased
|
|
||||||
* compilation times.
|
|
||||||
*
|
|
||||||
* When a new header is added to either the `benchmark` folder, or to
|
|
||||||
* the corresponding internal (detail) subfolder, it should be added here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CATCH_BENCHMARK_ALL_HPP_INCLUDED
|
|
||||||
#define CATCH_BENCHMARK_ALL_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_benchmark.hpp>
|
|
||||||
#include <catch2/benchmark/catch_chronometer.hpp>
|
|
||||||
#include <catch2/benchmark/catch_clock.hpp>
|
|
||||||
#include <catch2/benchmark/catch_constructor.hpp>
|
|
||||||
#include <catch2/benchmark/catch_environment.hpp>
|
|
||||||
#include <catch2/benchmark/catch_estimate.hpp>
|
|
||||||
#include <catch2/benchmark/catch_execution_plan.hpp>
|
|
||||||
#include <catch2/benchmark/catch_optimizer.hpp>
|
|
||||||
#include <catch2/benchmark/catch_outlier_classification.hpp>
|
|
||||||
#include <catch2/benchmark/catch_sample_analysis.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_analyse.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_estimate_clock.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_measure.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_repeat.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_stats.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_timing.hpp>
|
|
||||||
|
|
||||||
#endif // CATCH_BENCHMARK_ALL_HPP_INCLUDED
|
|
@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_chronometer.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
ChronometerConcept::~ChronometerConcept() = default;
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
@ -1,74 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_CHRONOMETER_HPP_INCLUDED
|
|
||||||
#define CATCH_CHRONOMETER_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_clock.hpp>
|
|
||||||
#include <catch2/benchmark/catch_optimizer.hpp>
|
|
||||||
#include <catch2/internal/catch_meta.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
struct ChronometerConcept {
|
|
||||||
virtual void start() = 0;
|
|
||||||
virtual void finish() = 0;
|
|
||||||
virtual ~ChronometerConcept(); // = default;
|
|
||||||
|
|
||||||
ChronometerConcept() = default;
|
|
||||||
ChronometerConcept(ChronometerConcept const&) = default;
|
|
||||||
ChronometerConcept& operator=(ChronometerConcept const&) = default;
|
|
||||||
};
|
|
||||||
template <typename Clock>
|
|
||||||
struct ChronometerModel final : public ChronometerConcept {
|
|
||||||
void start() override { started = Clock::now(); }
|
|
||||||
void finish() override { finished = Clock::now(); }
|
|
||||||
|
|
||||||
ClockDuration<Clock> elapsed() const { return finished - started; }
|
|
||||||
|
|
||||||
TimePoint<Clock> started;
|
|
||||||
TimePoint<Clock> finished;
|
|
||||||
};
|
|
||||||
} // namespace Detail
|
|
||||||
|
|
||||||
struct Chronometer {
|
|
||||||
public:
|
|
||||||
template <typename Fun>
|
|
||||||
void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable<Fun(int)>()); }
|
|
||||||
|
|
||||||
int runs() const { return repeats; }
|
|
||||||
|
|
||||||
Chronometer(Detail::ChronometerConcept& meter, int repeats_)
|
|
||||||
: impl(&meter)
|
|
||||||
, repeats(repeats_) {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename Fun>
|
|
||||||
void measure(Fun&& fun, std::false_type) {
|
|
||||||
measure([&fun](int) { return fun(); }, std::true_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Fun>
|
|
||||||
void measure(Fun&& fun, std::true_type) {
|
|
||||||
Detail::optimizer_barrier();
|
|
||||||
impl->start();
|
|
||||||
for (int i = 0; i < repeats; ++i) invoke_deoptimized(fun, i);
|
|
||||||
impl->finish();
|
|
||||||
Detail::optimizer_barrier();
|
|
||||||
}
|
|
||||||
|
|
||||||
Detail::ChronometerConcept* impl;
|
|
||||||
int repeats;
|
|
||||||
};
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_CHRONOMETER_HPP_INCLUDED
|
|
@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_CLOCK_HPP_INCLUDED
|
|
||||||
#define CATCH_CLOCK_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <ratio>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
template <typename Clock>
|
|
||||||
using ClockDuration = typename Clock::duration;
|
|
||||||
template <typename Clock>
|
|
||||||
using FloatDuration = std::chrono::duration<double, typename Clock::period>;
|
|
||||||
|
|
||||||
template <typename Clock>
|
|
||||||
using TimePoint = typename Clock::time_point;
|
|
||||||
|
|
||||||
using default_clock = std::chrono::steady_clock;
|
|
||||||
|
|
||||||
template <typename Clock>
|
|
||||||
struct now {
|
|
||||||
TimePoint<Clock> operator()() const {
|
|
||||||
return Clock::now();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_CLOCK_HPP_INCLUDED
|
|
@ -1,82 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_CONSTRUCTOR_HPP_INCLUDED
|
|
||||||
#define CATCH_CONSTRUCTOR_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
template <typename T, bool Destruct>
|
|
||||||
struct ObjectStorage
|
|
||||||
{
|
|
||||||
ObjectStorage() = default;
|
|
||||||
|
|
||||||
ObjectStorage(const ObjectStorage& other)
|
|
||||||
{
|
|
||||||
new(&data) T(other.stored_object());
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectStorage(ObjectStorage&& other)
|
|
||||||
{
|
|
||||||
new(data) T(CATCH_MOVE(other.stored_object()));
|
|
||||||
}
|
|
||||||
|
|
||||||
~ObjectStorage() { destruct_on_exit<T>(); }
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
void construct(Args&&... args)
|
|
||||||
{
|
|
||||||
new (data) T(CATCH_FORWARD(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool AllowManualDestruction = !Destruct>
|
|
||||||
std::enable_if_t<AllowManualDestruction> destruct()
|
|
||||||
{
|
|
||||||
stored_object().~T();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// If this is a constructor benchmark, destruct the underlying object
|
|
||||||
template <typename U>
|
|
||||||
void destruct_on_exit(std::enable_if_t<Destruct, U>* = nullptr) { destruct<true>(); }
|
|
||||||
// Otherwise, don't
|
|
||||||
template <typename U>
|
|
||||||
void destruct_on_exit(std::enable_if_t<!Destruct, U>* = nullptr) { }
|
|
||||||
|
|
||||||
#if defined( __GNUC__ ) && __GNUC__ <= 6
|
|
||||||
# pragma GCC diagnostic push
|
|
||||||
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
|
||||||
#endif
|
|
||||||
T& stored_object() { return *reinterpret_cast<T*>( data ); }
|
|
||||||
|
|
||||||
T const& stored_object() const {
|
|
||||||
return *reinterpret_cast<T const*>( data );
|
|
||||||
}
|
|
||||||
#if defined( __GNUC__ ) && __GNUC__ <= 6
|
|
||||||
# pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
alignas( T ) unsigned char data[sizeof( T )]{};
|
|
||||||
};
|
|
||||||
} // namespace Detail
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using storage_for = Detail::ObjectStorage<T, true>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using destructable_object = Detail::ObjectStorage<T, false>;
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_CONSTRUCTOR_HPP_INCLUDED
|
|
@ -1,37 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED
|
|
||||||
#define CATCH_ENVIRONMENT_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_clock.hpp>
|
|
||||||
#include <catch2/benchmark/catch_outlier_classification.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
template <typename Duration>
|
|
||||||
struct EnvironmentEstimate {
|
|
||||||
Duration mean;
|
|
||||||
OutlierClassification outliers;
|
|
||||||
|
|
||||||
template <typename Duration2>
|
|
||||||
operator EnvironmentEstimate<Duration2>() const {
|
|
||||||
return { mean, outliers };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <typename Clock>
|
|
||||||
struct Environment {
|
|
||||||
using clock_type = Clock;
|
|
||||||
EnvironmentEstimate<FloatDuration<Clock>> clock_resolution;
|
|
||||||
EnvironmentEstimate<FloatDuration<Clock>> clock_cost;
|
|
||||||
};
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_ENVIRONMENT_HPP_INCLUDED
|
|
@ -1,30 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_ESTIMATE_HPP_INCLUDED
|
|
||||||
#define CATCH_ESTIMATE_HPP_INCLUDED
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
template <typename Duration>
|
|
||||||
struct Estimate {
|
|
||||||
Duration point;
|
|
||||||
Duration lower_bound;
|
|
||||||
Duration upper_bound;
|
|
||||||
double confidence_interval;
|
|
||||||
|
|
||||||
template <typename Duration2>
|
|
||||||
operator Estimate<Duration2>() const {
|
|
||||||
return { point, lower_bound, upper_bound, confidence_interval };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_ESTIMATE_HPP_INCLUDED
|
|
@ -1,60 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
|
||||||
#define CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
|
||||||
#include <catch2/benchmark/catch_clock.hpp>
|
|
||||||
#include <catch2/benchmark/catch_environment.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_repeat.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
template <typename Duration>
|
|
||||||
struct ExecutionPlan {
|
|
||||||
int iterations_per_sample;
|
|
||||||
Duration estimated_duration;
|
|
||||||
Detail::BenchmarkFunction benchmark;
|
|
||||||
Duration warmup_time;
|
|
||||||
int warmup_iterations;
|
|
||||||
|
|
||||||
template <typename Duration2>
|
|
||||||
operator ExecutionPlan<Duration2>() const {
|
|
||||||
return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations };
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Clock>
|
|
||||||
std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
|
|
||||||
// warmup a bit
|
|
||||||
Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{}));
|
|
||||||
|
|
||||||
std::vector<FloatDuration<Clock>> times;
|
|
||||||
const auto num_samples = cfg.benchmarkSamples();
|
|
||||||
times.reserve( num_samples );
|
|
||||||
for ( size_t i = 0; i < num_samples; ++i ) {
|
|
||||||
Detail::ChronometerModel<Clock> model;
|
|
||||||
this->benchmark( Chronometer( model, iterations_per_sample ) );
|
|
||||||
auto sample_time = model.elapsed() - env.clock_cost.mean;
|
|
||||||
if ( sample_time < FloatDuration<Clock>::zero() ) {
|
|
||||||
sample_time = FloatDuration<Clock>::zero();
|
|
||||||
}
|
|
||||||
times.push_back(sample_time / iterations_per_sample);
|
|
||||||
}
|
|
||||||
return times;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
|
@ -1,78 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_OPTIMIZER_HPP_INCLUDED
|
|
||||||
#define CATCH_OPTIMIZER_HPP_INCLUDED
|
|
||||||
|
|
||||||
#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
|
|
||||||
# include <atomic> // atomic_thread_fence
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
template <typename T>
|
|
||||||
inline void keep_memory(T* p) {
|
|
||||||
asm volatile("" : : "g"(p) : "memory");
|
|
||||||
}
|
|
||||||
inline void keep_memory() {
|
|
||||||
asm volatile("" : : : "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Detail {
|
|
||||||
inline void optimizer_barrier() { keep_memory(); }
|
|
||||||
} // namespace Detail
|
|
||||||
#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
|
|
||||||
|
|
||||||
#if defined(_MSVC_VER)
|
|
||||||
#pragma optimize("", off)
|
|
||||||
#elif defined(__IAR_SYSTEMS_ICC__)
|
|
||||||
// For IAR the pragma only affects the following function
|
|
||||||
#pragma optimize=disable
|
|
||||||
#endif
|
|
||||||
template <typename T>
|
|
||||||
inline void keep_memory(T* p) {
|
|
||||||
// thanks @milleniumbug
|
|
||||||
*reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
|
|
||||||
}
|
|
||||||
// TODO equivalent keep_memory()
|
|
||||||
#if defined(_MSVC_VER)
|
|
||||||
#pragma optimize("", on)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Detail {
|
|
||||||
inline void optimizer_barrier() {
|
|
||||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
|
||||||
}
|
|
||||||
} // namespace Detail
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline void deoptimize_value(T&& x) {
|
|
||||||
keep_memory(&x);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Fn, typename... Args>
|
|
||||||
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<!std::is_same<void, decltype(fn(args...))>::value> {
|
|
||||||
deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Fn, typename... Args>
|
|
||||||
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
|
|
||||||
CATCH_FORWARD(fn) (CATCH_FORWARD(args)...);
|
|
||||||
}
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_OPTIMIZER_HPP_INCLUDED
|
|
@ -1,29 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED
|
|
||||||
#define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
struct OutlierClassification {
|
|
||||||
int samples_seen = 0;
|
|
||||||
int low_severe = 0; // more than 3 times IQR below Q1
|
|
||||||
int low_mild = 0; // 1.5 to 3 times IQR below Q1
|
|
||||||
int high_mild = 0; // 1.5 to 3 times IQR above Q3
|
|
||||||
int high_severe = 0; // more than 3 times IQR above Q3
|
|
||||||
|
|
||||||
int total() const {
|
|
||||||
return low_severe + low_mild + high_mild + high_severe;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED
|
|
@ -1,48 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
|
|
||||||
#define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_estimate.hpp>
|
|
||||||
#include <catch2/benchmark/catch_outlier_classification.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
template <typename Duration>
|
|
||||||
struct SampleAnalysis {
|
|
||||||
std::vector<Duration> samples;
|
|
||||||
Estimate<Duration> mean;
|
|
||||||
Estimate<Duration> standard_deviation;
|
|
||||||
OutlierClassification outliers;
|
|
||||||
double outlier_variance;
|
|
||||||
|
|
||||||
template <typename Duration2>
|
|
||||||
operator SampleAnalysis<Duration2>() const {
|
|
||||||
std::vector<Duration2> samples2;
|
|
||||||
samples2.reserve(samples.size());
|
|
||||||
for (auto const& d : samples) {
|
|
||||||
samples2.push_back(Duration2(d));
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
CATCH_MOVE(samples2),
|
|
||||||
mean,
|
|
||||||
standard_deviation,
|
|
||||||
outliers,
|
|
||||||
outlier_variance,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
|
|
@ -1,82 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_ANALYSE_HPP_INCLUDED
|
|
||||||
#define CATCH_ANALYSE_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_environment.hpp>
|
|
||||||
#include <catch2/benchmark/catch_sample_analysis.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_stats.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
template <typename Duration, typename Iterator>
|
|
||||||
SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) {
|
|
||||||
if (!cfg.benchmarkNoAnalysis()) {
|
|
||||||
std::vector<double> samples;
|
|
||||||
samples.reserve(static_cast<size_t>(last - first));
|
|
||||||
for (auto current = first; current != last; ++current) {
|
|
||||||
samples.push_back( current->count() );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end());
|
|
||||||
auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end());
|
|
||||||
|
|
||||||
auto wrap_estimate = [](Estimate<double> e) {
|
|
||||||
return Estimate<Duration> {
|
|
||||||
Duration(e.point),
|
|
||||||
Duration(e.lower_bound),
|
|
||||||
Duration(e.upper_bound),
|
|
||||||
e.confidence_interval,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
std::vector<Duration> samples2;
|
|
||||||
samples2.reserve(samples.size());
|
|
||||||
for (auto s : samples) {
|
|
||||||
samples2.push_back( Duration( s ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
CATCH_MOVE(samples2),
|
|
||||||
wrap_estimate(analysis.mean),
|
|
||||||
wrap_estimate(analysis.standard_deviation),
|
|
||||||
outliers,
|
|
||||||
analysis.outlier_variance,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
std::vector<Duration> samples;
|
|
||||||
samples.reserve(static_cast<size_t>(last - first));
|
|
||||||
|
|
||||||
Duration mean = Duration(0);
|
|
||||||
int i = 0;
|
|
||||||
for (auto it = first; it < last; ++it, ++i) {
|
|
||||||
samples.push_back(Duration(*it));
|
|
||||||
mean += Duration(*it);
|
|
||||||
}
|
|
||||||
mean /= i;
|
|
||||||
|
|
||||||
return {
|
|
||||||
CATCH_MOVE(samples),
|
|
||||||
Estimate<Duration>{mean, mean, mean, 0.0},
|
|
||||||
Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
|
|
||||||
OutlierClassification{},
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_ANALYSE_HPP_INCLUDED
|
|
@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
|
|
||||||
#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
BenchmarkFunction::callable::~callable() = default;
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
@ -1,107 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
|
|
||||||
#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_chronometer.hpp>
|
|
||||||
#include <catch2/internal/catch_meta.hpp>
|
|
||||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
template <typename T, typename U>
|
|
||||||
struct is_related
|
|
||||||
: std::is_same<std::decay_t<T>, std::decay_t<U>> {};
|
|
||||||
|
|
||||||
/// We need to reinvent std::function because every piece of code that might add overhead
|
|
||||||
/// in a measurement context needs to have consistent performance characteristics so that we
|
|
||||||
/// can account for it in the measurement.
|
|
||||||
/// Implementations of std::function with optimizations that aren't always applicable, like
|
|
||||||
/// small buffer optimizations, are not uncommon.
|
|
||||||
/// This is effectively an implementation of std::function without any such optimizations;
|
|
||||||
/// it may be slow, but it is consistently slow.
|
|
||||||
struct BenchmarkFunction {
|
|
||||||
private:
|
|
||||||
struct callable {
|
|
||||||
virtual void call(Chronometer meter) const = 0;
|
|
||||||
virtual Catch::Detail::unique_ptr<callable> clone() const = 0;
|
|
||||||
virtual ~callable(); // = default;
|
|
||||||
|
|
||||||
callable() = default;
|
|
||||||
callable(callable const&) = default;
|
|
||||||
callable& operator=(callable const&) = default;
|
|
||||||
};
|
|
||||||
template <typename Fun>
|
|
||||||
struct model : public callable {
|
|
||||||
model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
|
|
||||||
model(Fun const& fun_) : fun(fun_) {}
|
|
||||||
|
|
||||||
Catch::Detail::unique_ptr<callable> clone() const override {
|
|
||||||
return Catch::Detail::make_unique<model<Fun>>( *this );
|
|
||||||
}
|
|
||||||
|
|
||||||
void call(Chronometer meter) const override {
|
|
||||||
call(meter, is_callable<Fun(Chronometer)>());
|
|
||||||
}
|
|
||||||
void call(Chronometer meter, std::true_type) const {
|
|
||||||
fun(meter);
|
|
||||||
}
|
|
||||||
void call(Chronometer meter, std::false_type) const {
|
|
||||||
meter.measure(fun);
|
|
||||||
}
|
|
||||||
|
|
||||||
Fun fun;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct do_nothing { void operator()() const {} };
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
BenchmarkFunction(model<T>* c) : f(c) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
BenchmarkFunction()
|
|
||||||
: f(new model<do_nothing>{ {} }) {}
|
|
||||||
|
|
||||||
template <typename Fun,
|
|
||||||
std::enable_if_t<!is_related<Fun, BenchmarkFunction>::value, int> = 0>
|
|
||||||
BenchmarkFunction(Fun&& fun)
|
|
||||||
: f(new model<std::decay_t<Fun>>(CATCH_FORWARD(fun))) {}
|
|
||||||
|
|
||||||
BenchmarkFunction( BenchmarkFunction&& that ) noexcept:
|
|
||||||
f( CATCH_MOVE( that.f ) ) {}
|
|
||||||
|
|
||||||
BenchmarkFunction(BenchmarkFunction const& that)
|
|
||||||
: f(that.f->clone()) {}
|
|
||||||
|
|
||||||
BenchmarkFunction&
|
|
||||||
operator=( BenchmarkFunction&& that ) noexcept {
|
|
||||||
f = CATCH_MOVE( that.f );
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BenchmarkFunction& operator=(BenchmarkFunction const& that) {
|
|
||||||
f = that.f->clone();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(Chronometer meter) const { f->call(meter); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Catch::Detail::unique_ptr<callable> f;
|
|
||||||
};
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
|
|
@ -1,64 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED
|
|
||||||
#define CATCH_BENCHMARK_STATS_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
#include <catch2/benchmark/catch_estimate.hpp>
|
|
||||||
#include <catch2/benchmark/catch_outlier_classification.hpp>
|
|
||||||
// The fwd decl & default specialization needs to be seen by VS2017 before
|
|
||||||
// BenchmarkStats itself, or VS2017 will report compilation error.
|
|
||||||
#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
struct BenchmarkInfo {
|
|
||||||
std::string name;
|
|
||||||
double estimatedDuration;
|
|
||||||
int iterations;
|
|
||||||
unsigned int samples;
|
|
||||||
unsigned int resamples;
|
|
||||||
double clockResolution;
|
|
||||||
double clockCost;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class Duration>
|
|
||||||
struct BenchmarkStats {
|
|
||||||
BenchmarkInfo info;
|
|
||||||
|
|
||||||
std::vector<Duration> samples;
|
|
||||||
Benchmark::Estimate<Duration> mean;
|
|
||||||
Benchmark::Estimate<Duration> standardDeviation;
|
|
||||||
Benchmark::OutlierClassification outliers;
|
|
||||||
double outlierVariance;
|
|
||||||
|
|
||||||
template <typename Duration2>
|
|
||||||
operator BenchmarkStats<Duration2>() const {
|
|
||||||
std::vector<Duration2> samples2;
|
|
||||||
samples2.reserve(samples.size());
|
|
||||||
for (auto const& sample : samples) {
|
|
||||||
samples2.push_back(Duration2(sample));
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
info,
|
|
||||||
CATCH_MOVE(samples2),
|
|
||||||
mean,
|
|
||||||
standardDeviation,
|
|
||||||
outliers,
|
|
||||||
outlierVariance,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED
|
|
@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
|
|
||||||
#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
// We cannot forward declare the type with default template argument
|
|
||||||
// multiple times, so it is split out into a separate header so that
|
|
||||||
// we can prevent multiple declarations in dependees
|
|
||||||
template <typename Duration = std::chrono::duration<double, std::nano>>
|
|
||||||
struct BenchmarkStats;
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
|
|
@ -1,58 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED
|
|
||||||
#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_meta.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
template <typename T>
|
|
||||||
struct CompleteType { using type = T; };
|
|
||||||
template <>
|
|
||||||
struct CompleteType<void> { struct type {}; };
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using CompleteType_t = typename CompleteType<T>::type;
|
|
||||||
|
|
||||||
template <typename Result>
|
|
||||||
struct CompleteInvoker {
|
|
||||||
template <typename Fun, typename... Args>
|
|
||||||
static Result invoke(Fun&& fun, Args&&... args) {
|
|
||||||
return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <>
|
|
||||||
struct CompleteInvoker<void> {
|
|
||||||
template <typename Fun, typename... Args>
|
|
||||||
static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) {
|
|
||||||
CATCH_FORWARD(fun)(CATCH_FORWARD(args)...);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// invoke and not return void :(
|
|
||||||
template <typename Fun, typename... Args>
|
|
||||||
CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) {
|
|
||||||
return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Detail
|
|
||||||
|
|
||||||
template <typename Fun>
|
|
||||||
Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) {
|
|
||||||
return Detail::complete_invoke(CATCH_FORWARD(fun));
|
|
||||||
}
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED
|
|
@ -1,125 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_ESTIMATE_CLOCK_HPP_INCLUDED
|
|
||||||
#define CATCH_ESTIMATE_CLOCK_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_clock.hpp>
|
|
||||||
#include <catch2/benchmark/catch_environment.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_stats.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_measure.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
|
|
||||||
#include <catch2/benchmark/catch_clock.hpp>
|
|
||||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
template <typename Clock>
|
|
||||||
std::vector<double> resolution(int k) {
|
|
||||||
std::vector<TimePoint<Clock>> times;
|
|
||||||
times.reserve(static_cast<size_t>(k + 1));
|
|
||||||
for ( int i = 0; i < k + 1; ++i ) {
|
|
||||||
times.push_back( Clock::now() );
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<double> deltas;
|
|
||||||
deltas.reserve(static_cast<size_t>(k));
|
|
||||||
for ( size_t idx = 1; idx < times.size(); ++idx ) {
|
|
||||||
deltas.push_back( static_cast<double>(
|
|
||||||
( times[idx] - times[idx - 1] ).count() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return deltas;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto warmup_iterations = 10000;
|
|
||||||
constexpr auto warmup_time = std::chrono::milliseconds(100);
|
|
||||||
constexpr auto minimum_ticks = 1000;
|
|
||||||
constexpr auto warmup_seed = 10000;
|
|
||||||
constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
|
|
||||||
constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
|
|
||||||
constexpr auto clock_cost_estimation_tick_limit = 100000;
|
|
||||||
constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10);
|
|
||||||
constexpr auto clock_cost_estimation_iterations = 10000;
|
|
||||||
|
|
||||||
template <typename Clock>
|
|
||||||
int warmup() {
|
|
||||||
return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>)
|
|
||||||
.iterations;
|
|
||||||
}
|
|
||||||
template <typename Clock>
|
|
||||||
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) {
|
|
||||||
auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
|
|
||||||
.result;
|
|
||||||
return {
|
|
||||||
FloatDuration<Clock>(mean(r.begin(), r.end())),
|
|
||||||
classify_outliers(r.begin(), r.end()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
template <typename Clock>
|
|
||||||
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
|
|
||||||
auto time_limit = (std::min)(
|
|
||||||
resolution * clock_cost_estimation_tick_limit,
|
|
||||||
FloatDuration<Clock>(clock_cost_estimation_time_limit));
|
|
||||||
auto time_clock = [](int k) {
|
|
||||||
return Detail::measure<Clock>([k] {
|
|
||||||
for (int i = 0; i < k; ++i) {
|
|
||||||
volatile auto ignored = Clock::now();
|
|
||||||
(void)ignored;
|
|
||||||
}
|
|
||||||
}).elapsed;
|
|
||||||
};
|
|
||||||
time_clock(1);
|
|
||||||
int iters = clock_cost_estimation_iterations;
|
|
||||||
auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock);
|
|
||||||
std::vector<double> times;
|
|
||||||
int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
|
|
||||||
times.reserve(static_cast<size_t>(nsamples));
|
|
||||||
for ( int s = 0; s < nsamples; ++s ) {
|
|
||||||
times.push_back( static_cast<double>(
|
|
||||||
( time_clock( r.iterations ) / r.iterations )
|
|
||||||
.count() ) );
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
FloatDuration<Clock>(mean(times.begin(), times.end())),
|
|
||||||
classify_outliers(times.begin(), times.end()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Clock>
|
|
||||||
Environment<FloatDuration<Clock>> measure_environment() {
|
|
||||||
#if defined(__clang__)
|
|
||||||
# pragma clang diagnostic push
|
|
||||||
# pragma clang diagnostic ignored "-Wexit-time-destructors"
|
|
||||||
#endif
|
|
||||||
static Catch::Detail::unique_ptr<Environment<FloatDuration<Clock>>> env;
|
|
||||||
#if defined(__clang__)
|
|
||||||
# pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
if (env) {
|
|
||||||
return *env;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto iters = Detail::warmup<Clock>();
|
|
||||||
auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
|
|
||||||
auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
|
|
||||||
|
|
||||||
env = Catch::Detail::make_unique<Environment<FloatDuration<Clock>>>( Environment<FloatDuration<Clock>>{resolution, cost} );
|
|
||||||
return *env;
|
|
||||||
}
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_ESTIMATE_CLOCK_HPP_INCLUDED
|
|
@ -1,32 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_MEASURE_HPP_INCLUDED
|
|
||||||
#define CATCH_MEASURE_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_timing.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
template <typename Clock, typename Fun, typename... Args>
|
|
||||||
TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) {
|
|
||||||
auto start = Clock::now();
|
|
||||||
auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...);
|
|
||||||
auto end = Clock::now();
|
|
||||||
auto delta = end - start;
|
|
||||||
return { delta, CATCH_FORWARD(r), 1 };
|
|
||||||
}
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_MEASURE_HPP_INCLUDED
|
|
@ -1,36 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_REPEAT_HPP_INCLUDED
|
|
||||||
#define CATCH_REPEAT_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
template <typename Fun>
|
|
||||||
struct repeater {
|
|
||||||
void operator()(int k) const {
|
|
||||||
for (int i = 0; i < k; ++i) {
|
|
||||||
fun();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Fun fun;
|
|
||||||
};
|
|
||||||
template <typename Fun>
|
|
||||||
repeater<std::decay_t<Fun>> repeat(Fun&& fun) {
|
|
||||||
return { CATCH_FORWARD(fun) };
|
|
||||||
}
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_REPEAT_HPP_INCLUDED
|
|
@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
|
|
||||||
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
|
|
||||||
#include <catch2/internal/catch_enforce.hpp>
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
struct optimized_away_error : std::exception {
|
|
||||||
const char* what() const noexcept override;
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* optimized_away_error::what() const noexcept {
|
|
||||||
return "could not measure benchmark, maybe it was optimized away";
|
|
||||||
}
|
|
||||||
|
|
||||||
void throw_optimized_away_error() {
|
|
||||||
Catch::throw_exception(optimized_away_error{});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
@ -1,65 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
|
||||||
#define CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_clock.hpp>
|
|
||||||
#include <catch2/benchmark/catch_chronometer.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_measure.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_timing.hpp>
|
|
||||||
#include <catch2/internal/catch_meta.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
template <typename Clock, typename Fun>
|
|
||||||
TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) {
|
|
||||||
return Detail::measure<Clock>(fun, iters);
|
|
||||||
}
|
|
||||||
template <typename Clock, typename Fun>
|
|
||||||
TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) {
|
|
||||||
Detail::ChronometerModel<Clock> meter;
|
|
||||||
auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
|
|
||||||
|
|
||||||
return { meter.elapsed(), CATCH_MOVE(result), iters };
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Clock, typename Fun>
|
|
||||||
using run_for_at_least_argument_t = std::conditional_t<is_callable<Fun(Chronometer)>::value, Chronometer, int>;
|
|
||||||
|
|
||||||
|
|
||||||
[[noreturn]]
|
|
||||||
void throw_optimized_away_error();
|
|
||||||
|
|
||||||
template <typename Clock, typename Fun>
|
|
||||||
TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>>
|
|
||||||
run_for_at_least(ClockDuration<Clock> how_long,
|
|
||||||
const int initial_iterations,
|
|
||||||
Fun&& fun) {
|
|
||||||
auto iters = initial_iterations;
|
|
||||||
while (iters < (1 << 30)) {
|
|
||||||
auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>());
|
|
||||||
|
|
||||||
if (Timing.elapsed >= how_long) {
|
|
||||||
return { Timing.elapsed, CATCH_MOVE(Timing.result), iters };
|
|
||||||
}
|
|
||||||
iters *= 2;
|
|
||||||
}
|
|
||||||
throw_optimized_away_error();
|
|
||||||
}
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
|
@ -1,330 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#include <catch2/benchmark/detail/catch_stats.hpp>
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <numeric>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_USE_ASYNC)
|
|
||||||
#include <future>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template <typename URng, typename Estimator>
|
|
||||||
static sample
|
|
||||||
resample( URng& rng,
|
|
||||||
unsigned int resamples,
|
|
||||||
std::vector<double>::const_iterator first,
|
|
||||||
std::vector<double>::const_iterator last,
|
|
||||||
Estimator& estimator ) {
|
|
||||||
auto n = static_cast<size_t>( last - first );
|
|
||||||
std::uniform_int_distribution<decltype( n )> dist( 0,
|
|
||||||
n - 1 );
|
|
||||||
|
|
||||||
sample out;
|
|
||||||
out.reserve( resamples );
|
|
||||||
// We allocate the vector outside the loop to avoid realloc
|
|
||||||
// per resample
|
|
||||||
std::vector<double> resampled;
|
|
||||||
resampled.reserve( n );
|
|
||||||
for ( size_t i = 0; i < resamples; ++i ) {
|
|
||||||
resampled.clear();
|
|
||||||
for ( size_t s = 0; s < n; ++s ) {
|
|
||||||
resampled.push_back(
|
|
||||||
first[static_cast<std::ptrdiff_t>(
|
|
||||||
dist( rng ) )] );
|
|
||||||
}
|
|
||||||
const auto estimate =
|
|
||||||
estimator( resampled.begin(), resampled.end() );
|
|
||||||
out.push_back( estimate );
|
|
||||||
}
|
|
||||||
std::sort( out.begin(), out.end() );
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double outlier_variance( Estimate<double> mean,
|
|
||||||
Estimate<double> stddev,
|
|
||||||
int n ) {
|
|
||||||
double sb = stddev.point;
|
|
||||||
double mn = mean.point / n;
|
|
||||||
double mg_min = mn / 2.;
|
|
||||||
double sg = (std::min)( mg_min / 4., sb / std::sqrt( n ) );
|
|
||||||
double sg2 = sg * sg;
|
|
||||||
double sb2 = sb * sb;
|
|
||||||
|
|
||||||
auto c_max = [n, mn, sb2, sg2]( double x ) -> double {
|
|
||||||
double k = mn - x;
|
|
||||||
double d = k * k;
|
|
||||||
double nd = n * d;
|
|
||||||
double k0 = -n * nd;
|
|
||||||
double k1 = sb2 - n * sg2 + nd;
|
|
||||||
double det = k1 * k1 - 4 * sg2 * k0;
|
|
||||||
return static_cast<int>( -2. * k0 /
|
|
||||||
( k1 + std::sqrt( det ) ) );
|
|
||||||
};
|
|
||||||
|
|
||||||
auto var_out = [n, sb2, sg2]( double c ) {
|
|
||||||
double nc = n - c;
|
|
||||||
return ( nc / n ) * ( sb2 - nc * sg2 );
|
|
||||||
};
|
|
||||||
|
|
||||||
return (std::min)( var_out( 1 ),
|
|
||||||
var_out(
|
|
||||||
(std::min)( c_max( 0. ),
|
|
||||||
c_max( mg_min ) ) ) ) /
|
|
||||||
sb2;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double erf_inv( double x ) {
|
|
||||||
// Code accompanying the article "Approximating the erfinv
|
|
||||||
// function" in GPU Computing Gems, Volume 2
|
|
||||||
double w, p;
|
|
||||||
|
|
||||||
w = -log( ( 1.0 - x ) * ( 1.0 + x ) );
|
|
||||||
|
|
||||||
if ( w < 6.250000 ) {
|
|
||||||
w = w - 3.125000;
|
|
||||||
p = -3.6444120640178196996e-21;
|
|
||||||
p = -1.685059138182016589e-19 + p * w;
|
|
||||||
p = 1.2858480715256400167e-18 + p * w;
|
|
||||||
p = 1.115787767802518096e-17 + p * w;
|
|
||||||
p = -1.333171662854620906e-16 + p * w;
|
|
||||||
p = 2.0972767875968561637e-17 + p * w;
|
|
||||||
p = 6.6376381343583238325e-15 + p * w;
|
|
||||||
p = -4.0545662729752068639e-14 + p * w;
|
|
||||||
p = -8.1519341976054721522e-14 + p * w;
|
|
||||||
p = 2.6335093153082322977e-12 + p * w;
|
|
||||||
p = -1.2975133253453532498e-11 + p * w;
|
|
||||||
p = -5.4154120542946279317e-11 + p * w;
|
|
||||||
p = 1.051212273321532285e-09 + p * w;
|
|
||||||
p = -4.1126339803469836976e-09 + p * w;
|
|
||||||
p = -2.9070369957882005086e-08 + p * w;
|
|
||||||
p = 4.2347877827932403518e-07 + p * w;
|
|
||||||
p = -1.3654692000834678645e-06 + p * w;
|
|
||||||
p = -1.3882523362786468719e-05 + p * w;
|
|
||||||
p = 0.0001867342080340571352 + p * w;
|
|
||||||
p = -0.00074070253416626697512 + p * w;
|
|
||||||
p = -0.0060336708714301490533 + p * w;
|
|
||||||
p = 0.24015818242558961693 + p * w;
|
|
||||||
p = 1.6536545626831027356 + p * w;
|
|
||||||
} else if ( w < 16.000000 ) {
|
|
||||||
w = sqrt( w ) - 3.250000;
|
|
||||||
p = 2.2137376921775787049e-09;
|
|
||||||
p = 9.0756561938885390979e-08 + p * w;
|
|
||||||
p = -2.7517406297064545428e-07 + p * w;
|
|
||||||
p = 1.8239629214389227755e-08 + p * w;
|
|
||||||
p = 1.5027403968909827627e-06 + p * w;
|
|
||||||
p = -4.013867526981545969e-06 + p * w;
|
|
||||||
p = 2.9234449089955446044e-06 + p * w;
|
|
||||||
p = 1.2475304481671778723e-05 + p * w;
|
|
||||||
p = -4.7318229009055733981e-05 + p * w;
|
|
||||||
p = 6.8284851459573175448e-05 + p * w;
|
|
||||||
p = 2.4031110387097893999e-05 + p * w;
|
|
||||||
p = -0.0003550375203628474796 + p * w;
|
|
||||||
p = 0.00095328937973738049703 + p * w;
|
|
||||||
p = -0.0016882755560235047313 + p * w;
|
|
||||||
p = 0.0024914420961078508066 + p * w;
|
|
||||||
p = -0.0037512085075692412107 + p * w;
|
|
||||||
p = 0.005370914553590063617 + p * w;
|
|
||||||
p = 1.0052589676941592334 + p * w;
|
|
||||||
p = 3.0838856104922207635 + p * w;
|
|
||||||
} else {
|
|
||||||
w = sqrt( w ) - 5.000000;
|
|
||||||
p = -2.7109920616438573243e-11;
|
|
||||||
p = -2.5556418169965252055e-10 + p * w;
|
|
||||||
p = 1.5076572693500548083e-09 + p * w;
|
|
||||||
p = -3.7894654401267369937e-09 + p * w;
|
|
||||||
p = 7.6157012080783393804e-09 + p * w;
|
|
||||||
p = -1.4960026627149240478e-08 + p * w;
|
|
||||||
p = 2.9147953450901080826e-08 + p * w;
|
|
||||||
p = -6.7711997758452339498e-08 + p * w;
|
|
||||||
p = 2.2900482228026654717e-07 + p * w;
|
|
||||||
p = -9.9298272942317002539e-07 + p * w;
|
|
||||||
p = 4.5260625972231537039e-06 + p * w;
|
|
||||||
p = -1.9681778105531670567e-05 + p * w;
|
|
||||||
p = 7.5995277030017761139e-05 + p * w;
|
|
||||||
p = -0.00021503011930044477347 + p * w;
|
|
||||||
p = -0.00013871931833623122026 + p * w;
|
|
||||||
p = 1.0103004648645343977 + p * w;
|
|
||||||
p = 4.8499064014085844221 + p * w;
|
|
||||||
}
|
|
||||||
return p * x;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double
|
|
||||||
standard_deviation( std::vector<double>::const_iterator first,
|
|
||||||
std::vector<double>::const_iterator last ) {
|
|
||||||
auto m = Catch::Benchmark::Detail::mean( first, last );
|
|
||||||
double variance =
|
|
||||||
std::accumulate( first,
|
|
||||||
last,
|
|
||||||
0.,
|
|
||||||
[m]( double a, double b ) {
|
|
||||||
double diff = b - m;
|
|
||||||
return a + diff * diff;
|
|
||||||
} ) /
|
|
||||||
( last - first );
|
|
||||||
return std::sqrt( variance );
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
|
|
||||||
#if defined( __GNUC__ ) || defined( __clang__ )
|
|
||||||
# pragma GCC diagnostic push
|
|
||||||
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
|
||||||
#endif
|
|
||||||
bool directCompare( double lhs, double rhs ) { return lhs == rhs; }
|
|
||||||
#if defined( __GNUC__ ) || defined( __clang__ )
|
|
||||||
# pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) {
|
|
||||||
auto count = last - first;
|
|
||||||
double idx = (count - 1) * k / static_cast<double>(q);
|
|
||||||
int j = static_cast<int>(idx);
|
|
||||||
double g = idx - j;
|
|
||||||
std::nth_element(first, first + j, last);
|
|
||||||
auto xj = first[j];
|
|
||||||
if ( directCompare( g, 0 ) ) {
|
|
||||||
return xj;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto xj1 = *std::min_element(first + (j + 1), last);
|
|
||||||
return xj + g * (xj1 - xj);
|
|
||||||
}
|
|
||||||
|
|
||||||
OutlierClassification
|
|
||||||
classify_outliers( std::vector<double>::const_iterator first,
|
|
||||||
std::vector<double>::const_iterator last ) {
|
|
||||||
std::vector<double> copy( first, last );
|
|
||||||
|
|
||||||
auto q1 = weighted_average_quantile( 1, 4, copy.begin(), copy.end() );
|
|
||||||
auto q3 = weighted_average_quantile( 3, 4, copy.begin(), copy.end() );
|
|
||||||
auto iqr = q3 - q1;
|
|
||||||
auto los = q1 - ( iqr * 3. );
|
|
||||||
auto lom = q1 - ( iqr * 1.5 );
|
|
||||||
auto him = q3 + ( iqr * 1.5 );
|
|
||||||
auto his = q3 + ( iqr * 3. );
|
|
||||||
|
|
||||||
OutlierClassification o;
|
|
||||||
for ( ; first != last; ++first ) {
|
|
||||||
const double t = *first;
|
|
||||||
if ( t < los ) {
|
|
||||||
++o.low_severe;
|
|
||||||
} else if ( t < lom ) {
|
|
||||||
++o.low_mild;
|
|
||||||
} else if ( t > his ) {
|
|
||||||
++o.high_severe;
|
|
||||||
} else if ( t > him ) {
|
|
||||||
++o.high_mild;
|
|
||||||
}
|
|
||||||
++o.samples_seen;
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
double mean( std::vector<double>::const_iterator first,
|
|
||||||
std::vector<double>::const_iterator last ) {
|
|
||||||
auto count = last - first;
|
|
||||||
double sum = 0.;
|
|
||||||
while (first != last) {
|
|
||||||
sum += *first;
|
|
||||||
++first;
|
|
||||||
}
|
|
||||||
return sum / static_cast<double>(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
double erfc_inv(double x) {
|
|
||||||
return erf_inv(1.0 - x);
|
|
||||||
}
|
|
||||||
|
|
||||||
double normal_quantile(double p) {
|
|
||||||
static const double ROOT_TWO = std::sqrt(2.0);
|
|
||||||
|
|
||||||
double result = 0.0;
|
|
||||||
assert(p >= 0 && p <= 1);
|
|
||||||
if (p < 0 || p > 1) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = -erfc_inv(2.0 * p);
|
|
||||||
// result *= normal distribution standard deviation (1.0) * sqrt(2)
|
|
||||||
result *= /*sd * */ ROOT_TWO;
|
|
||||||
// result += normal disttribution mean (0)
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bootstrap_analysis analyse_samples(double confidence_level,
|
|
||||||
unsigned int n_resamples,
|
|
||||||
std::vector<double>::iterator first,
|
|
||||||
std::vector<double>::iterator last) {
|
|
||||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
|
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
|
||||||
static std::random_device entropy;
|
|
||||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
|
||||||
|
|
||||||
auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
|
|
||||||
|
|
||||||
auto mean = &Detail::mean;
|
|
||||||
auto stddev = &standard_deviation;
|
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_USE_ASYNC)
|
|
||||||
auto Estimate = [=](double(*f)(std::vector<double>::const_iterator,
|
|
||||||
std::vector<double>::const_iterator)) {
|
|
||||||
auto seed = entropy();
|
|
||||||
return std::async(std::launch::async, [=] {
|
|
||||||
std::mt19937 rng(seed);
|
|
||||||
auto resampled = resample(rng, n_resamples, first, last, f);
|
|
||||||
return bootstrap(confidence_level, first, last, resampled, f);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
auto mean_future = Estimate(mean);
|
|
||||||
auto stddev_future = Estimate(stddev);
|
|
||||||
|
|
||||||
auto mean_estimate = mean_future.get();
|
|
||||||
auto stddev_estimate = stddev_future.get();
|
|
||||||
#else
|
|
||||||
auto Estimate = [=](double(*f)(std::vector<double>::const_iterator,
|
|
||||||
std::vector<double>::const_iterator)) {
|
|
||||||
auto seed = entropy();
|
|
||||||
std::mt19937 rng(seed);
|
|
||||||
auto resampled = resample(rng, n_resamples, first, last, f);
|
|
||||||
return bootstrap(confidence_level, first, last, resampled, f);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto mean_estimate = Estimate(mean);
|
|
||||||
auto stddev_estimate = Estimate(stddev);
|
|
||||||
#endif // CATCH_USE_ASYNC
|
|
||||||
|
|
||||||
double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n);
|
|
||||||
|
|
||||||
return { mean_estimate, stddev_estimate, outlier_variance };
|
|
||||||
}
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
@ -1,125 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_STATS_HPP_INCLUDED
|
|
||||||
#define CATCH_STATS_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_estimate.hpp>
|
|
||||||
#include <catch2/benchmark/catch_outlier_classification.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
namespace Detail {
|
|
||||||
using sample = std::vector<double>;
|
|
||||||
|
|
||||||
// Used when we know we want == comparison of two doubles
|
|
||||||
// to centralize warning suppression
|
|
||||||
bool directCompare( double lhs, double rhs );
|
|
||||||
|
|
||||||
double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
|
|
||||||
|
|
||||||
OutlierClassification
|
|
||||||
classify_outliers( std::vector<double>::const_iterator first,
|
|
||||||
std::vector<double>::const_iterator last );
|
|
||||||
|
|
||||||
double mean( std::vector<double>::const_iterator first,
|
|
||||||
std::vector<double>::const_iterator last );
|
|
||||||
|
|
||||||
template <typename Estimator>
|
|
||||||
sample jackknife(Estimator&& estimator,
|
|
||||||
std::vector<double>::iterator first,
|
|
||||||
std::vector<double>::iterator last) {
|
|
||||||
auto n = static_cast<size_t>(last - first);
|
|
||||||
auto second = first;
|
|
||||||
++second;
|
|
||||||
sample results;
|
|
||||||
results.reserve(n);
|
|
||||||
|
|
||||||
for (auto it = first; it != last; ++it) {
|
|
||||||
std::iter_swap(it, first);
|
|
||||||
results.push_back(estimator(second, last));
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline double normal_cdf(double x) {
|
|
||||||
return std::erfc(-x / std::sqrt(2.0)) / 2.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double erfc_inv(double x);
|
|
||||||
|
|
||||||
double normal_quantile(double p);
|
|
||||||
|
|
||||||
template <typename Estimator>
|
|
||||||
Estimate<double> bootstrap( double confidence_level,
|
|
||||||
std::vector<double>::iterator first,
|
|
||||||
std::vector<double>::iterator last,
|
|
||||||
sample const& resample,
|
|
||||||
Estimator&& estimator ) {
|
|
||||||
auto n_samples = last - first;
|
|
||||||
|
|
||||||
double point = estimator(first, last);
|
|
||||||
// Degenerate case with a single sample
|
|
||||||
if (n_samples == 1) return { point, point, point, confidence_level };
|
|
||||||
|
|
||||||
sample jack = jackknife(estimator, first, last);
|
|
||||||
double jack_mean = mean(jack.begin(), jack.end());
|
|
||||||
double sum_squares = 0, sum_cubes = 0;
|
|
||||||
for (double x : jack) {
|
|
||||||
auto difference = jack_mean - x;
|
|
||||||
auto square = difference * difference;
|
|
||||||
auto cube = square * difference;
|
|
||||||
sum_squares += square; sum_cubes += cube;
|
|
||||||
}
|
|
||||||
|
|
||||||
double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
|
|
||||||
long n = static_cast<long>(resample.size());
|
|
||||||
double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / static_cast<double>(n);
|
|
||||||
// degenerate case with uniform samples
|
|
||||||
if ( directCompare( prob_n, 0. ) ) {
|
|
||||||
return { point, point, point, confidence_level };
|
|
||||||
}
|
|
||||||
|
|
||||||
double bias = normal_quantile(prob_n);
|
|
||||||
double z1 = normal_quantile((1. - confidence_level) / 2.);
|
|
||||||
|
|
||||||
auto cumn = [n]( double x ) -> long {
|
|
||||||
return std::lround( normal_cdf( x ) * static_cast<double>(n) );
|
|
||||||
};
|
|
||||||
auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
|
|
||||||
double b1 = bias + z1;
|
|
||||||
double b2 = bias - z1;
|
|
||||||
double a1 = a(b1);
|
|
||||||
double a2 = a(b2);
|
|
||||||
auto lo = static_cast<size_t>((std::max)(cumn(a1), 0l));
|
|
||||||
auto hi = static_cast<size_t>((std::min)(cumn(a2), n - 1));
|
|
||||||
|
|
||||||
return { point, resample[lo], resample[hi], confidence_level };
|
|
||||||
}
|
|
||||||
|
|
||||||
struct bootstrap_analysis {
|
|
||||||
Estimate<double> mean;
|
|
||||||
Estimate<double> standard_deviation;
|
|
||||||
double outlier_variance;
|
|
||||||
};
|
|
||||||
|
|
||||||
bootstrap_analysis analyse_samples(double confidence_level,
|
|
||||||
unsigned int n_resamples,
|
|
||||||
std::vector<double>::iterator first,
|
|
||||||
std::vector<double>::iterator last);
|
|
||||||
} // namespace Detail
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_STATS_HPP_INCLUDED
|
|
@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
// Adapted from donated nonius code.
|
|
||||||
|
|
||||||
#ifndef CATCH_TIMING_HPP_INCLUDED
|
|
||||||
#define CATCH_TIMING_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_clock.hpp>
|
|
||||||
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Benchmark {
|
|
||||||
template <typename Duration, typename Result>
|
|
||||||
struct Timing {
|
|
||||||
Duration elapsed;
|
|
||||||
Result result;
|
|
||||||
int iterations;
|
|
||||||
};
|
|
||||||
template <typename Clock, typename Func, typename... Args>
|
|
||||||
using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
|
|
||||||
} // namespace Benchmark
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_TIMING_HPP_INCLUDED
|
|
@ -1,129 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
/** \file
|
|
||||||
* This is a convenience header for Catch2. It includes **all** of Catch2 headers.
|
|
||||||
*
|
|
||||||
* Generally the Catch2 users should use specific includes they need,
|
|
||||||
* but this header can be used instead for ease-of-experimentation, or
|
|
||||||
* just plain convenience, at the cost of (significantly) increased
|
|
||||||
* compilation times.
|
|
||||||
*
|
|
||||||
* When a new header is added to either the top level folder, or to the
|
|
||||||
* corresponding internal subfolder, it should be added here. Headers
|
|
||||||
* added to the various subparts (e.g. matchers, generators, etc...),
|
|
||||||
* should go their respective catch-all headers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CATCH_ALL_HPP_INCLUDED
|
|
||||||
#define CATCH_ALL_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/benchmark/catch_benchmark_all.hpp>
|
|
||||||
#include <catch2/catch_approx.hpp>
|
|
||||||
#include <catch2/catch_assertion_info.hpp>
|
|
||||||
#include <catch2/catch_assertion_result.hpp>
|
|
||||||
#include <catch2/catch_config.hpp>
|
|
||||||
#include <catch2/catch_get_random_seed.hpp>
|
|
||||||
#include <catch2/catch_message.hpp>
|
|
||||||
#include <catch2/catch_section_info.hpp>
|
|
||||||
#include <catch2/catch_session.hpp>
|
|
||||||
#include <catch2/catch_tag_alias.hpp>
|
|
||||||
#include <catch2/catch_tag_alias_autoregistrar.hpp>
|
|
||||||
#include <catch2/catch_template_test_macros.hpp>
|
|
||||||
#include <catch2/catch_test_case_info.hpp>
|
|
||||||
#include <catch2/catch_test_macros.hpp>
|
|
||||||
#include <catch2/catch_test_spec.hpp>
|
|
||||||
#include <catch2/catch_timer.hpp>
|
|
||||||
#include <catch2/catch_tostring.hpp>
|
|
||||||
#include <catch2/catch_totals.hpp>
|
|
||||||
#include <catch2/catch_translate_exception.hpp>
|
|
||||||
#include <catch2/catch_version.hpp>
|
|
||||||
#include <catch2/catch_version_macros.hpp>
|
|
||||||
#include <catch2/generators/catch_generators_all.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_all.hpp>
|
|
||||||
#include <catch2/internal/catch_assertion_handler.hpp>
|
|
||||||
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
|
|
||||||
#include <catch2/internal/catch_case_sensitive.hpp>
|
|
||||||
#include <catch2/internal/catch_clara.hpp>
|
|
||||||
#include <catch2/internal/catch_commandline.hpp>
|
|
||||||
#include <catch2/internal/catch_compare_traits.hpp>
|
|
||||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
|
||||||
#include <catch2/internal/catch_config_android_logwrite.hpp>
|
|
||||||
#include <catch2/internal/catch_config_counter.hpp>
|
|
||||||
#include <catch2/internal/catch_config_static_analysis_support.hpp>
|
|
||||||
#include <catch2/internal/catch_config_uncaught_exceptions.hpp>
|
|
||||||
#include <catch2/internal/catch_config_wchar.hpp>
|
|
||||||
#include <catch2/internal/catch_console_colour.hpp>
|
|
||||||
#include <catch2/internal/catch_console_width.hpp>
|
|
||||||
#include <catch2/internal/catch_container_nonmembers.hpp>
|
|
||||||
#include <catch2/internal/catch_context.hpp>
|
|
||||||
#include <catch2/internal/catch_debug_console.hpp>
|
|
||||||
#include <catch2/internal/catch_debugger.hpp>
|
|
||||||
#include <catch2/internal/catch_decomposer.hpp>
|
|
||||||
#include <catch2/internal/catch_enforce.hpp>
|
|
||||||
#include <catch2/internal/catch_enum_values_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_errno_guard.hpp>
|
|
||||||
#include <catch2/internal/catch_exception_translator_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_fatal_condition_handler.hpp>
|
|
||||||
#include <catch2/internal/catch_floating_point_helpers.hpp>
|
|
||||||
#include <catch2/internal/catch_getenv.hpp>
|
|
||||||
#include <catch2/internal/catch_is_permutation.hpp>
|
|
||||||
#include <catch2/internal/catch_istream.hpp>
|
|
||||||
#include <catch2/internal/catch_lazy_expr.hpp>
|
|
||||||
#include <catch2/internal/catch_leak_detector.hpp>
|
|
||||||
#include <catch2/internal/catch_list.hpp>
|
|
||||||
#include <catch2/internal/catch_logical_traits.hpp>
|
|
||||||
#include <catch2/internal/catch_message_info.hpp>
|
|
||||||
#include <catch2/internal/catch_meta.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
#include <catch2/internal/catch_noncopyable.hpp>
|
|
||||||
#include <catch2/internal/catch_optional.hpp>
|
|
||||||
#include <catch2/internal/catch_output_redirect.hpp>
|
|
||||||
#include <catch2/internal/catch_parse_numbers.hpp>
|
|
||||||
#include <catch2/internal/catch_platform.hpp>
|
|
||||||
#include <catch2/internal/catch_polyfills.hpp>
|
|
||||||
#include <catch2/internal/catch_preprocessor.hpp>
|
|
||||||
#include <catch2/internal/catch_preprocessor_internal_stringify.hpp>
|
|
||||||
#include <catch2/internal/catch_preprocessor_remove_parens.hpp>
|
|
||||||
#include <catch2/internal/catch_random_number_generator.hpp>
|
|
||||||
#include <catch2/internal/catch_random_seed_generation.hpp>
|
|
||||||
#include <catch2/internal/catch_reporter_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_reporter_spec_parser.hpp>
|
|
||||||
#include <catch2/internal/catch_result_type.hpp>
|
|
||||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
|
||||||
#include <catch2/internal/catch_run_context.hpp>
|
|
||||||
#include <catch2/internal/catch_section.hpp>
|
|
||||||
#include <catch2/internal/catch_sharding.hpp>
|
|
||||||
#include <catch2/internal/catch_singletons.hpp>
|
|
||||||
#include <catch2/internal/catch_source_line_info.hpp>
|
|
||||||
#include <catch2/internal/catch_startup_exception_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_stdstreams.hpp>
|
|
||||||
#include <catch2/internal/catch_stream_end_stop.hpp>
|
|
||||||
#include <catch2/internal/catch_string_manip.hpp>
|
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
|
||||||
#include <catch2/internal/catch_tag_alias_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_template_test_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_test_case_info_hasher.hpp>
|
|
||||||
#include <catch2/internal/catch_test_case_registry_impl.hpp>
|
|
||||||
#include <catch2/internal/catch_test_case_tracker.hpp>
|
|
||||||
#include <catch2/internal/catch_test_failure_exception.hpp>
|
|
||||||
#include <catch2/internal/catch_test_macro_impl.hpp>
|
|
||||||
#include <catch2/internal/catch_test_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_test_run_info.hpp>
|
|
||||||
#include <catch2/internal/catch_test_spec_parser.hpp>
|
|
||||||
#include <catch2/internal/catch_textflow.hpp>
|
|
||||||
#include <catch2/internal/catch_to_string.hpp>
|
|
||||||
#include <catch2/internal/catch_uncaught_exceptions.hpp>
|
|
||||||
#include <catch2/internal/catch_unique_name.hpp>
|
|
||||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
|
||||||
#include <catch2/internal/catch_void_type.hpp>
|
|
||||||
#include <catch2/internal/catch_wildcard_pattern.hpp>
|
|
||||||
#include <catch2/internal/catch_xmlwriter.hpp>
|
|
||||||
#include <catch2/matchers/catch_matchers_all.hpp>
|
|
||||||
#include <catch2/reporters/catch_reporters_all.hpp>
|
|
||||||
|
|
||||||
#endif // CATCH_ALL_HPP_INCLUDED
|
|
@ -1,85 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#include <catch2/catch_approx.hpp>
|
|
||||||
#include <catch2/internal/catch_enforce.hpp>
|
|
||||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
|
|
||||||
// But without the subtraction to allow for INFINITY in comparison
|
|
||||||
bool marginComparison(double lhs, double rhs, double margin) {
|
|
||||||
return (lhs + margin >= rhs) && (rhs + margin >= lhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
Approx::Approx ( double value )
|
|
||||||
: m_epsilon( std::numeric_limits<float>::epsilon()*100. ),
|
|
||||||
m_margin( 0.0 ),
|
|
||||||
m_scale( 0.0 ),
|
|
||||||
m_value( value )
|
|
||||||
{}
|
|
||||||
|
|
||||||
Approx Approx::custom() {
|
|
||||||
return Approx( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
Approx Approx::operator-() const {
|
|
||||||
auto temp(*this);
|
|
||||||
temp.m_value = -temp.m_value;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::string Approx::toString() const {
|
|
||||||
ReusableStringStream rss;
|
|
||||||
rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
|
|
||||||
return rss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Approx::equalityComparisonImpl(const double other) const {
|
|
||||||
// First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
|
|
||||||
// Thanks to Richard Harris for his help refining the scaled margin value
|
|
||||||
return marginComparison(m_value, other, m_margin)
|
|
||||||
|| marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Approx::setMargin(double newMargin) {
|
|
||||||
CATCH_ENFORCE(newMargin >= 0,
|
|
||||||
"Invalid Approx::margin: " << newMargin << '.'
|
|
||||||
<< " Approx::Margin has to be non-negative.");
|
|
||||||
m_margin = newMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Approx::setEpsilon(double newEpsilon) {
|
|
||||||
CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
|
|
||||||
"Invalid Approx::epsilon: " << newEpsilon << '.'
|
|
||||||
<< " Approx::epsilon has to be in [0, 1]");
|
|
||||||
m_epsilon = newEpsilon;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace literals {
|
|
||||||
Approx operator "" _a(long double val) {
|
|
||||||
return Approx(val);
|
|
||||||
}
|
|
||||||
Approx operator "" _a(unsigned long long val) {
|
|
||||||
return Approx(val);
|
|
||||||
}
|
|
||||||
} // end namespace literals
|
|
||||||
|
|
||||||
std::string StringMaker<Catch::Approx>::convert(Catch::Approx const& value) {
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
@ -1,128 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_APPROX_HPP_INCLUDED
|
|
||||||
#define CATCH_APPROX_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/catch_tostring.hpp>
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
class Approx {
|
|
||||||
private:
|
|
||||||
bool equalityComparisonImpl(double other) const;
|
|
||||||
// Sets and validates the new margin (margin >= 0)
|
|
||||||
void setMargin(double margin);
|
|
||||||
// Sets and validates the new epsilon (0 < epsilon < 1)
|
|
||||||
void setEpsilon(double epsilon);
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Approx ( double value );
|
|
||||||
|
|
||||||
static Approx custom();
|
|
||||||
|
|
||||||
Approx operator-() const;
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
Approx operator()( T const& value ) const {
|
|
||||||
Approx approx( static_cast<double>(value) );
|
|
||||||
approx.m_epsilon = m_epsilon;
|
|
||||||
approx.m_margin = m_margin;
|
|
||||||
approx.m_scale = m_scale;
|
|
||||||
return approx;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
explicit Approx( T const& value ): Approx(static_cast<double>(value))
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
friend bool operator == ( const T& lhs, Approx const& rhs ) {
|
|
||||||
auto lhs_v = static_cast<double>(lhs);
|
|
||||||
return rhs.equalityComparisonImpl(lhs_v);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
friend bool operator == ( Approx const& lhs, const T& rhs ) {
|
|
||||||
return operator==( rhs, lhs );
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
friend bool operator != ( T const& lhs, Approx const& rhs ) {
|
|
||||||
return !operator==( lhs, rhs );
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
friend bool operator != ( Approx const& lhs, T const& rhs ) {
|
|
||||||
return !operator==( rhs, lhs );
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
friend bool operator <= ( T const& lhs, Approx const& rhs ) {
|
|
||||||
return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
friend bool operator <= ( Approx const& lhs, T const& rhs ) {
|
|
||||||
return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
friend bool operator >= ( T const& lhs, Approx const& rhs ) {
|
|
||||||
return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
friend bool operator >= ( Approx const& lhs, T const& rhs ) {
|
|
||||||
return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
Approx& epsilon( T const& newEpsilon ) {
|
|
||||||
const auto epsilonAsDouble = static_cast<double>(newEpsilon);
|
|
||||||
setEpsilon(epsilonAsDouble);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
Approx& margin( T const& newMargin ) {
|
|
||||||
const auto marginAsDouble = static_cast<double>(newMargin);
|
|
||||||
setMargin(marginAsDouble);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
|
||||||
Approx& scale( T const& newScale ) {
|
|
||||||
m_scale = static_cast<double>(newScale);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string toString() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
double m_epsilon;
|
|
||||||
double m_margin;
|
|
||||||
double m_scale;
|
|
||||||
double m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace literals {
|
|
||||||
Approx operator ""_a(long double val);
|
|
||||||
Approx operator ""_a(unsigned long long val);
|
|
||||||
} // end namespace literals
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct StringMaker<Catch::Approx> {
|
|
||||||
static std::string convert(Catch::Approx const& value);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_APPROX_HPP_INCLUDED
|
|
@ -1,28 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED
|
|
||||||
#define CATCH_ASSERTION_INFO_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_result_type.hpp>
|
|
||||||
#include <catch2/internal/catch_source_line_info.hpp>
|
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
struct AssertionInfo {
|
|
||||||
// AssertionInfo() = delete;
|
|
||||||
|
|
||||||
StringRef macroName;
|
|
||||||
SourceLineInfo lineInfo;
|
|
||||||
StringRef capturedExpression;
|
|
||||||
ResultDisposition::Flags resultDisposition;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED
|
|
@ -1,105 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#include <catch2/catch_assertion_result.hpp>
|
|
||||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression):
|
|
||||||
lazyExpression(_lazyExpression),
|
|
||||||
resultType(_resultType) {}
|
|
||||||
|
|
||||||
std::string AssertionResultData::reconstructExpression() const {
|
|
||||||
|
|
||||||
if( reconstructedExpression.empty() ) {
|
|
||||||
if( lazyExpression ) {
|
|
||||||
ReusableStringStream rss;
|
|
||||||
rss << lazyExpression;
|
|
||||||
reconstructedExpression = rss.str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reconstructedExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData&& data )
|
|
||||||
: m_info( info ),
|
|
||||||
m_resultData( CATCH_MOVE(data) )
|
|
||||||
{}
|
|
||||||
|
|
||||||
// Result was a success
|
|
||||||
bool AssertionResult::succeeded() const {
|
|
||||||
return Catch::isOk( m_resultData.resultType );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result was a success, or failure is suppressed
|
|
||||||
bool AssertionResult::isOk() const {
|
|
||||||
return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultWas::OfType AssertionResult::getResultType() const {
|
|
||||||
return m_resultData.resultType;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AssertionResult::hasExpression() const {
|
|
||||||
return !m_info.capturedExpression.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AssertionResult::hasMessage() const {
|
|
||||||
return !m_resultData.message.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string AssertionResult::getExpression() const {
|
|
||||||
// Possibly overallocating by 3 characters should be basically free
|
|
||||||
std::string expr; expr.reserve(m_info.capturedExpression.size() + 3);
|
|
||||||
if (isFalseTest(m_info.resultDisposition)) {
|
|
||||||
expr += "!(";
|
|
||||||
}
|
|
||||||
expr += m_info.capturedExpression;
|
|
||||||
if (isFalseTest(m_info.resultDisposition)) {
|
|
||||||
expr += ')';
|
|
||||||
}
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string AssertionResult::getExpressionInMacro() const {
|
|
||||||
if ( m_info.macroName.empty() ) {
|
|
||||||
return static_cast<std::string>( m_info.capturedExpression );
|
|
||||||
}
|
|
||||||
std::string expr;
|
|
||||||
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
|
|
||||||
expr += m_info.macroName;
|
|
||||||
expr += "( ";
|
|
||||||
expr += m_info.capturedExpression;
|
|
||||||
expr += " )";
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AssertionResult::hasExpandedExpression() const {
|
|
||||||
return hasExpression() && getExpandedExpression() != getExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string AssertionResult::getExpandedExpression() const {
|
|
||||||
std::string expr = m_resultData.reconstructExpression();
|
|
||||||
return expr.empty()
|
|
||||||
? getExpression()
|
|
||||||
: expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringRef AssertionResult::getMessage() const {
|
|
||||||
return m_resultData.message;
|
|
||||||
}
|
|
||||||
SourceLineInfo AssertionResult::getSourceInfo() const {
|
|
||||||
return m_info.lineInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringRef AssertionResult::getTestMacroName() const {
|
|
||||||
return m_info.macroName;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
@ -1,60 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED
|
|
||||||
#define CATCH_ASSERTION_RESULT_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/catch_assertion_info.hpp>
|
|
||||||
#include <catch2/internal/catch_result_type.hpp>
|
|
||||||
#include <catch2/internal/catch_source_line_info.hpp>
|
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
|
||||||
#include <catch2/internal/catch_lazy_expr.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
struct AssertionResultData
|
|
||||||
{
|
|
||||||
AssertionResultData() = delete;
|
|
||||||
|
|
||||||
AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression );
|
|
||||||
|
|
||||||
std::string message;
|
|
||||||
mutable std::string reconstructedExpression;
|
|
||||||
LazyExpression lazyExpression;
|
|
||||||
ResultWas::OfType resultType;
|
|
||||||
|
|
||||||
std::string reconstructExpression() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AssertionResult {
|
|
||||||
public:
|
|
||||||
AssertionResult() = delete;
|
|
||||||
AssertionResult( AssertionInfo const& info, AssertionResultData&& data );
|
|
||||||
|
|
||||||
bool isOk() const;
|
|
||||||
bool succeeded() const;
|
|
||||||
ResultWas::OfType getResultType() const;
|
|
||||||
bool hasExpression() const;
|
|
||||||
bool hasMessage() const;
|
|
||||||
std::string getExpression() const;
|
|
||||||
std::string getExpressionInMacro() const;
|
|
||||||
bool hasExpandedExpression() const;
|
|
||||||
std::string getExpandedExpression() const;
|
|
||||||
StringRef getMessage() const;
|
|
||||||
SourceLineInfo getSourceInfo() const;
|
|
||||||
StringRef getTestMacroName() const;
|
|
||||||
|
|
||||||
//protected:
|
|
||||||
AssertionInfo m_info;
|
|
||||||
AssertionResultData m_resultData;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED
|
|
@ -1,247 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#include <catch2/catch_config.hpp>
|
|
||||||
#include <catch2/catch_user_config.hpp>
|
|
||||||
#include <catch2/internal/catch_enforce.hpp>
|
|
||||||
#include <catch2/internal/catch_parse_numbers.hpp>
|
|
||||||
#include <catch2/internal/catch_stdstreams.hpp>
|
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
|
||||||
#include <catch2/internal/catch_string_manip.hpp>
|
|
||||||
#include <catch2/internal/catch_test_spec_parser.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_getenv.hpp>
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
static bool enableBazelEnvSupport() {
|
|
||||||
#if defined( CATCH_CONFIG_BAZEL_SUPPORT )
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
return Detail::getEnv( "BAZEL_TEST" ) != nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
struct bazelShardingOptions {
|
|
||||||
unsigned int shardIndex, shardCount;
|
|
||||||
std::string shardFilePath;
|
|
||||||
};
|
|
||||||
|
|
||||||
static Optional<bazelShardingOptions> readBazelShardingOptions() {
|
|
||||||
const auto bazelShardIndex = Detail::getEnv( "TEST_SHARD_INDEX" );
|
|
||||||
const auto bazelShardTotal = Detail::getEnv( "TEST_TOTAL_SHARDS" );
|
|
||||||
const auto bazelShardInfoFile = Detail::getEnv( "TEST_SHARD_STATUS_FILE" );
|
|
||||||
|
|
||||||
|
|
||||||
const bool has_all =
|
|
||||||
bazelShardIndex && bazelShardTotal && bazelShardInfoFile;
|
|
||||||
if ( !has_all ) {
|
|
||||||
// We provide nice warning message if the input is
|
|
||||||
// misconfigured.
|
|
||||||
auto warn = []( const char* env_var ) {
|
|
||||||
Catch::cerr()
|
|
||||||
<< "Warning: Bazel shard configuration is missing '"
|
|
||||||
<< env_var << "'. Shard configuration is skipped.\n";
|
|
||||||
};
|
|
||||||
if ( !bazelShardIndex ) {
|
|
||||||
warn( "TEST_SHARD_INDEX" );
|
|
||||||
}
|
|
||||||
if ( !bazelShardTotal ) {
|
|
||||||
warn( "TEST_TOTAL_SHARDS" );
|
|
||||||
}
|
|
||||||
if ( !bazelShardInfoFile ) {
|
|
||||||
warn( "TEST_SHARD_STATUS_FILE" );
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto shardIndex = parseUInt( bazelShardIndex );
|
|
||||||
if ( !shardIndex ) {
|
|
||||||
Catch::cerr()
|
|
||||||
<< "Warning: could not parse 'TEST_SHARD_INDEX' ('" << bazelShardIndex
|
|
||||||
<< "') as unsigned int.\n";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
auto shardTotal = parseUInt( bazelShardTotal );
|
|
||||||
if ( !shardTotal ) {
|
|
||||||
Catch::cerr()
|
|
||||||
<< "Warning: could not parse 'TEST_TOTAL_SHARD' ('"
|
|
||||||
<< bazelShardTotal << "') as unsigned int.\n";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return bazelShardingOptions{
|
|
||||||
*shardIndex, *shardTotal, bazelShardInfoFile };
|
|
||||||
|
|
||||||
}
|
|
||||||
} // end namespace
|
|
||||||
|
|
||||||
|
|
||||||
bool operator==( ProcessedReporterSpec const& lhs,
|
|
||||||
ProcessedReporterSpec const& rhs ) {
|
|
||||||
return lhs.name == rhs.name &&
|
|
||||||
lhs.outputFilename == rhs.outputFilename &&
|
|
||||||
lhs.colourMode == rhs.colourMode &&
|
|
||||||
lhs.customOptions == rhs.customOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
Config::Config( ConfigData const& data ):
|
|
||||||
m_data( data ) {
|
|
||||||
// We need to trim filter specs to avoid trouble with superfluous
|
|
||||||
// whitespace (esp. important for bdd macros, as those are manually
|
|
||||||
// aligned with whitespace).
|
|
||||||
|
|
||||||
for (auto& elem : m_data.testsOrTags) {
|
|
||||||
elem = trim(elem);
|
|
||||||
}
|
|
||||||
for (auto& elem : m_data.sectionsToRun) {
|
|
||||||
elem = trim(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the default reporter if user hasn't asked for a specific one
|
|
||||||
if ( m_data.reporterSpecifications.empty() ) {
|
|
||||||
m_data.reporterSpecifications.push_back( {
|
|
||||||
#if defined( CATCH_CONFIG_DEFAULT_REPORTER )
|
|
||||||
CATCH_CONFIG_DEFAULT_REPORTER,
|
|
||||||
#else
|
|
||||||
"console",
|
|
||||||
#endif
|
|
||||||
{}, {}, {}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( enableBazelEnvSupport() ) {
|
|
||||||
readBazelEnvVars();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bazel support can modify the test specs, so parsing has to happen
|
|
||||||
// after reading Bazel env vars.
|
|
||||||
TestSpecParser parser( ITagAliasRegistry::get() );
|
|
||||||
if ( !m_data.testsOrTags.empty() ) {
|
|
||||||
m_hasTestFilters = true;
|
|
||||||
for ( auto const& testOrTags : m_data.testsOrTags ) {
|
|
||||||
parser.parse( testOrTags );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_testSpec = parser.testSpec();
|
|
||||||
|
|
||||||
|
|
||||||
// We now fixup the reporter specs to handle default output spec,
|
|
||||||
// default colour spec, etc
|
|
||||||
bool defaultOutputUsed = false;
|
|
||||||
for ( auto const& reporterSpec : m_data.reporterSpecifications ) {
|
|
||||||
// We do the default-output check separately, while always
|
|
||||||
// using the default output below to make the code simpler
|
|
||||||
// and avoid superfluous copies.
|
|
||||||
if ( reporterSpec.outputFile().none() ) {
|
|
||||||
CATCH_ENFORCE( !defaultOutputUsed,
|
|
||||||
"Internal error: cannot use default output for "
|
|
||||||
"multiple reporters" );
|
|
||||||
defaultOutputUsed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_processedReporterSpecs.push_back( ProcessedReporterSpec{
|
|
||||||
reporterSpec.name(),
|
|
||||||
reporterSpec.outputFile() ? *reporterSpec.outputFile()
|
|
||||||
: data.defaultOutputFilename,
|
|
||||||
reporterSpec.colourMode().valueOr( data.defaultColourMode ),
|
|
||||||
reporterSpec.customOptions() } );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Config::~Config() = default;
|
|
||||||
|
|
||||||
|
|
||||||
bool Config::listTests() const { return m_data.listTests; }
|
|
||||||
bool Config::listTags() const { return m_data.listTags; }
|
|
||||||
bool Config::listReporters() const { return m_data.listReporters; }
|
|
||||||
bool Config::listListeners() const { return m_data.listListeners; }
|
|
||||||
|
|
||||||
std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
|
|
||||||
std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
|
|
||||||
|
|
||||||
std::vector<ReporterSpec> const& Config::getReporterSpecs() const {
|
|
||||||
return m_data.reporterSpecifications;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ProcessedReporterSpec> const&
|
|
||||||
Config::getProcessedReporterSpecs() const {
|
|
||||||
return m_processedReporterSpecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestSpec const& Config::testSpec() const { return m_testSpec; }
|
|
||||||
bool Config::hasTestFilters() const { return m_hasTestFilters; }
|
|
||||||
|
|
||||||
bool Config::showHelp() const { return m_data.showHelp; }
|
|
||||||
|
|
||||||
// IConfig interface
|
|
||||||
bool Config::allowThrows() const { return !m_data.noThrow; }
|
|
||||||
StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
|
|
||||||
bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
|
|
||||||
bool Config::warnAboutMissingAssertions() const {
|
|
||||||
return !!( m_data.warnings & WarnAbout::NoAssertions );
|
|
||||||
}
|
|
||||||
bool Config::warnAboutUnmatchedTestSpecs() const {
|
|
||||||
return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec );
|
|
||||||
}
|
|
||||||
bool Config::zeroTestsCountAsSuccess() const { return m_data.allowZeroTests; }
|
|
||||||
ShowDurations Config::showDurations() const { return m_data.showDurations; }
|
|
||||||
double Config::minDuration() const { return m_data.minDuration; }
|
|
||||||
TestRunOrder Config::runOrder() const { return m_data.runOrder; }
|
|
||||||
uint32_t Config::rngSeed() const { return m_data.rngSeed; }
|
|
||||||
unsigned int Config::shardCount() const { return m_data.shardCount; }
|
|
||||||
unsigned int Config::shardIndex() const { return m_data.shardIndex; }
|
|
||||||
ColourMode Config::defaultColourMode() const { return m_data.defaultColourMode; }
|
|
||||||
bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; }
|
|
||||||
int Config::abortAfter() const { return m_data.abortAfter; }
|
|
||||||
bool Config::showInvisibles() const { return m_data.showInvisibles; }
|
|
||||||
Verbosity Config::verbosity() const { return m_data.verbosity; }
|
|
||||||
|
|
||||||
bool Config::skipBenchmarks() const { return m_data.skipBenchmarks; }
|
|
||||||
bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; }
|
|
||||||
unsigned int Config::benchmarkSamples() const { return m_data.benchmarkSamples; }
|
|
||||||
double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; }
|
|
||||||
unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; }
|
|
||||||
std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); }
|
|
||||||
|
|
||||||
void Config::readBazelEnvVars() {
|
|
||||||
// Register a JUnit reporter for Bazel. Bazel sets an environment
|
|
||||||
// variable with the path to XML output. If this file is written to
|
|
||||||
// during test, Bazel will not generate a default XML output.
|
|
||||||
// This allows the XML output file to contain higher level of detail
|
|
||||||
// than what is possible otherwise.
|
|
||||||
const auto bazelOutputFile = Detail::getEnv( "XML_OUTPUT_FILE" );
|
|
||||||
|
|
||||||
if ( bazelOutputFile ) {
|
|
||||||
m_data.reporterSpecifications.push_back(
|
|
||||||
{ "junit", std::string( bazelOutputFile ), {}, {} } );
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto bazelTestSpec = Detail::getEnv( "TESTBRIDGE_TEST_ONLY" );
|
|
||||||
if ( bazelTestSpec ) {
|
|
||||||
// Presumably the test spec from environment should overwrite
|
|
||||||
// the one we got from CLI (if we got any)
|
|
||||||
m_data.testsOrTags.clear();
|
|
||||||
m_data.testsOrTags.push_back( bazelTestSpec );
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto bazelShardOptions = readBazelShardingOptions();
|
|
||||||
if ( bazelShardOptions ) {
|
|
||||||
std::ofstream f( bazelShardOptions->shardFilePath,
|
|
||||||
std::ios_base::out | std::ios_base::trunc );
|
|
||||||
if ( f.is_open() ) {
|
|
||||||
f << "";
|
|
||||||
m_data.shardIndex = bazelShardOptions->shardIndex;
|
|
||||||
m_data.shardCount = bazelShardOptions->shardCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
@ -1,153 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_CONFIG_HPP_INCLUDED
|
|
||||||
#define CATCH_CONFIG_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/catch_test_spec.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
|
||||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
|
||||||
#include <catch2/internal/catch_optional.hpp>
|
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
|
||||||
#include <catch2/internal/catch_random_seed_generation.hpp>
|
|
||||||
#include <catch2/internal/catch_reporter_spec_parser.hpp>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
class IStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* `ReporterSpec` but with the defaults filled in.
|
|
||||||
*
|
|
||||||
* Like `ReporterSpec`, the semantics are unchecked.
|
|
||||||
*/
|
|
||||||
struct ProcessedReporterSpec {
|
|
||||||
std::string name;
|
|
||||||
std::string outputFilename;
|
|
||||||
ColourMode colourMode;
|
|
||||||
std::map<std::string, std::string> customOptions;
|
|
||||||
friend bool operator==( ProcessedReporterSpec const& lhs,
|
|
||||||
ProcessedReporterSpec const& rhs );
|
|
||||||
friend bool operator!=( ProcessedReporterSpec const& lhs,
|
|
||||||
ProcessedReporterSpec const& rhs ) {
|
|
||||||
return !( lhs == rhs );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ConfigData {
|
|
||||||
|
|
||||||
bool listTests = false;
|
|
||||||
bool listTags = false;
|
|
||||||
bool listReporters = false;
|
|
||||||
bool listListeners = false;
|
|
||||||
|
|
||||||
bool showSuccessfulTests = false;
|
|
||||||
bool shouldDebugBreak = false;
|
|
||||||
bool noThrow = false;
|
|
||||||
bool showHelp = false;
|
|
||||||
bool showInvisibles = false;
|
|
||||||
bool filenamesAsTags = false;
|
|
||||||
bool libIdentify = false;
|
|
||||||
bool allowZeroTests = false;
|
|
||||||
|
|
||||||
int abortAfter = -1;
|
|
||||||
uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default);
|
|
||||||
|
|
||||||
unsigned int shardCount = 1;
|
|
||||||
unsigned int shardIndex = 0;
|
|
||||||
|
|
||||||
bool skipBenchmarks = false;
|
|
||||||
bool benchmarkNoAnalysis = false;
|
|
||||||
unsigned int benchmarkSamples = 100;
|
|
||||||
double benchmarkConfidenceInterval = 0.95;
|
|
||||||
unsigned int benchmarkResamples = 100000;
|
|
||||||
std::chrono::milliseconds::rep benchmarkWarmupTime = 100;
|
|
||||||
|
|
||||||
Verbosity verbosity = Verbosity::Normal;
|
|
||||||
WarnAbout::What warnings = WarnAbout::Nothing;
|
|
||||||
ShowDurations showDurations = ShowDurations::DefaultForReporter;
|
|
||||||
double minDuration = -1;
|
|
||||||
TestRunOrder runOrder = TestRunOrder::Declared;
|
|
||||||
ColourMode defaultColourMode = ColourMode::PlatformDefault;
|
|
||||||
WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
|
|
||||||
|
|
||||||
std::string defaultOutputFilename;
|
|
||||||
std::string name;
|
|
||||||
std::string processName;
|
|
||||||
std::vector<ReporterSpec> reporterSpecifications;
|
|
||||||
|
|
||||||
std::vector<std::string> testsOrTags;
|
|
||||||
std::vector<std::string> sectionsToRun;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Config : public IConfig {
|
|
||||||
public:
|
|
||||||
|
|
||||||
Config() = default;
|
|
||||||
Config( ConfigData const& data );
|
|
||||||
~Config() override; // = default in the cpp file
|
|
||||||
|
|
||||||
bool listTests() const;
|
|
||||||
bool listTags() const;
|
|
||||||
bool listReporters() const;
|
|
||||||
bool listListeners() const;
|
|
||||||
|
|
||||||
std::vector<ReporterSpec> const& getReporterSpecs() const;
|
|
||||||
std::vector<ProcessedReporterSpec> const&
|
|
||||||
getProcessedReporterSpecs() const;
|
|
||||||
|
|
||||||
std::vector<std::string> const& getTestsOrTags() const override;
|
|
||||||
std::vector<std::string> const& getSectionsToRun() const override;
|
|
||||||
|
|
||||||
TestSpec const& testSpec() const override;
|
|
||||||
bool hasTestFilters() const override;
|
|
||||||
|
|
||||||
bool showHelp() const;
|
|
||||||
|
|
||||||
// IConfig interface
|
|
||||||
bool allowThrows() const override;
|
|
||||||
StringRef name() const override;
|
|
||||||
bool includeSuccessfulResults() const override;
|
|
||||||
bool warnAboutMissingAssertions() const override;
|
|
||||||
bool warnAboutUnmatchedTestSpecs() const override;
|
|
||||||
bool zeroTestsCountAsSuccess() const override;
|
|
||||||
ShowDurations showDurations() const override;
|
|
||||||
double minDuration() const override;
|
|
||||||
TestRunOrder runOrder() const override;
|
|
||||||
uint32_t rngSeed() const override;
|
|
||||||
unsigned int shardCount() const override;
|
|
||||||
unsigned int shardIndex() const override;
|
|
||||||
ColourMode defaultColourMode() const override;
|
|
||||||
bool shouldDebugBreak() const override;
|
|
||||||
int abortAfter() const override;
|
|
||||||
bool showInvisibles() const override;
|
|
||||||
Verbosity verbosity() const override;
|
|
||||||
bool skipBenchmarks() const override;
|
|
||||||
bool benchmarkNoAnalysis() const override;
|
|
||||||
unsigned int benchmarkSamples() const override;
|
|
||||||
double benchmarkConfidenceInterval() const override;
|
|
||||||
unsigned int benchmarkResamples() const override;
|
|
||||||
std::chrono::milliseconds benchmarkWarmupTime() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Reads Bazel env vars and applies them to the config
|
|
||||||
void readBazelEnvVars();
|
|
||||||
|
|
||||||
ConfigData m_data;
|
|
||||||
std::vector<ProcessedReporterSpec> m_processedReporterSpecs;
|
|
||||||
TestSpec m_testSpec;
|
|
||||||
bool m_hasTestFilters = false;
|
|
||||||
};
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_CONFIG_HPP_INCLUDED
|
|
@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
|
|
||||||
#include <catch2/catch_get_random_seed.hpp>
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_context.hpp>
|
|
||||||
#include <catch2/catch_config.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
std::uint32_t getSeed() {
|
|
||||||
return getCurrentContext().getConfig()->rngSeed();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_GET_RANDOM_SEED_HPP_INCLUDED
|
|
||||||
#define CATCH_GET_RANDOM_SEED_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
//! Returns Catch2's current RNG seed.
|
|
||||||
std::uint32_t getSeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CATCH_GET_RANDOM_SEED_HPP_INCLUDED
|
|
@ -1,116 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#include <catch2/catch_message.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
|
||||||
#include <catch2/internal/catch_uncaught_exceptions.hpp>
|
|
||||||
#include <catch2/internal/catch_enforce.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <stack>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
|
|
||||||
m_info( CATCH_MOVE(builder.m_info) ) {
|
|
||||||
m_info.message = builder.m_stream.str();
|
|
||||||
getResultCapture().pushScopedMessage( m_info );
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
|
|
||||||
m_info( CATCH_MOVE( old.m_info ) ) {
|
|
||||||
old.m_moved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedMessage::~ScopedMessage() {
|
|
||||||
if ( !uncaught_exceptions() && !m_moved ){
|
|
||||||
getResultCapture().popScopedMessage(m_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Capturer::Capturer( StringRef macroName,
|
|
||||||
SourceLineInfo const& lineInfo,
|
|
||||||
ResultWas::OfType resultType,
|
|
||||||
StringRef names ):
|
|
||||||
m_resultCapture( getResultCapture() ) {
|
|
||||||
auto trimmed = [&] (size_t start, size_t end) {
|
|
||||||
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
|
|
||||||
++start;
|
|
||||||
}
|
|
||||||
while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) {
|
|
||||||
--end;
|
|
||||||
}
|
|
||||||
return names.substr(start, end - start + 1);
|
|
||||||
};
|
|
||||||
auto skipq = [&] (size_t start, char quote) {
|
|
||||||
for (auto i = start + 1; i < names.size() ; ++i) {
|
|
||||||
if (names[i] == quote)
|
|
||||||
return i;
|
|
||||||
if (names[i] == '\\')
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote");
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t start = 0;
|
|
||||||
std::stack<char> openings;
|
|
||||||
for (size_t pos = 0; pos < names.size(); ++pos) {
|
|
||||||
char c = names[pos];
|
|
||||||
switch (c) {
|
|
||||||
case '[':
|
|
||||||
case '{':
|
|
||||||
case '(':
|
|
||||||
// It is basically impossible to disambiguate between
|
|
||||||
// comparison and start of template args in this context
|
|
||||||
// case '<':
|
|
||||||
openings.push(c);
|
|
||||||
break;
|
|
||||||
case ']':
|
|
||||||
case '}':
|
|
||||||
case ')':
|
|
||||||
// case '>':
|
|
||||||
openings.pop();
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
case '\'':
|
|
||||||
pos = skipq(pos, c);
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
if (start != pos && openings.empty()) {
|
|
||||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
|
||||||
m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
|
|
||||||
m_messages.back().message += " := ";
|
|
||||||
start = pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(openings.empty() && "Mismatched openings");
|
|
||||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
|
||||||
m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
|
|
||||||
m_messages.back().message += " := ";
|
|
||||||
}
|
|
||||||
Capturer::~Capturer() {
|
|
||||||
if ( !uncaught_exceptions() ){
|
|
||||||
assert( m_captured == m_messages.size() );
|
|
||||||
for( size_t i = 0; i < m_captured; ++i )
|
|
||||||
m_resultCapture.popScopedMessage( m_messages[i] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Capturer::captureValue( size_t index, std::string const& value ) {
|
|
||||||
assert( index < m_messages.size() );
|
|
||||||
m_messages[index].message += value;
|
|
||||||
m_resultCapture.pushScopedMessage( m_messages[index] );
|
|
||||||
m_captured++;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
@ -1,148 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_MESSAGE_HPP_INCLUDED
|
|
||||||
#define CATCH_MESSAGE_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_result_type.hpp>
|
|
||||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
|
||||||
#include <catch2/internal/catch_stream_end_stop.hpp>
|
|
||||||
#include <catch2/internal/catch_message_info.hpp>
|
|
||||||
#include <catch2/catch_tostring.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
struct SourceLineInfo;
|
|
||||||
class IResultCapture;
|
|
||||||
|
|
||||||
struct MessageStream {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
MessageStream& operator << ( T const& value ) {
|
|
||||||
m_stream << value;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReusableStringStream m_stream;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MessageBuilder : MessageStream {
|
|
||||||
MessageBuilder( StringRef macroName,
|
|
||||||
SourceLineInfo const& lineInfo,
|
|
||||||
ResultWas::OfType type ):
|
|
||||||
m_info(macroName, lineInfo, type) {}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
MessageBuilder&& operator << ( T const& value ) && {
|
|
||||||
m_stream << value;
|
|
||||||
return CATCH_MOVE(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageInfo m_info;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ScopedMessage {
|
|
||||||
public:
|
|
||||||
explicit ScopedMessage( MessageBuilder&& builder );
|
|
||||||
ScopedMessage( ScopedMessage& duplicate ) = delete;
|
|
||||||
ScopedMessage( ScopedMessage&& old ) noexcept;
|
|
||||||
~ScopedMessage();
|
|
||||||
|
|
||||||
MessageInfo m_info;
|
|
||||||
bool m_moved = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Capturer {
|
|
||||||
std::vector<MessageInfo> m_messages;
|
|
||||||
IResultCapture& m_resultCapture;
|
|
||||||
size_t m_captured = 0;
|
|
||||||
public:
|
|
||||||
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
|
|
||||||
|
|
||||||
Capturer(Capturer const&) = delete;
|
|
||||||
Capturer& operator=(Capturer const&) = delete;
|
|
||||||
|
|
||||||
~Capturer();
|
|
||||||
|
|
||||||
void captureValue( size_t index, std::string const& value );
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void captureValues( size_t index, T const& value ) {
|
|
||||||
captureValue( index, Catch::Detail::stringify( value ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename... Ts>
|
|
||||||
void captureValues( size_t index, T const& value, Ts const&... values ) {
|
|
||||||
captureValue( index, Catch::Detail::stringify(value) );
|
|
||||||
captureValues( index+1, values... );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
|
|
||||||
do { \
|
|
||||||
Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \
|
|
||||||
catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
|
|
||||||
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
|
||||||
} while( false )
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
|
|
||||||
Catch::Capturer varName( macroName##_catch_sr, \
|
|
||||||
CATCH_INTERNAL_LINEINFO, \
|
|
||||||
Catch::ResultWas::Info, \
|
|
||||||
#__VA_ARGS__##_catch_sr ); \
|
|
||||||
varName.captureValues( 0, __VA_ARGS__ )
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
#define INTERNAL_CATCH_INFO( macroName, log ) \
|
|
||||||
const Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
|
|
||||||
Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
|
|
||||||
|
|
||||||
#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
|
|
||||||
#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
|
|
||||||
#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
|
|
||||||
#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ )
|
|
||||||
|
|
||||||
#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
|
|
||||||
|
|
||||||
#define CATCH_INFO( msg ) (void)(0)
|
|
||||||
#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
|
|
||||||
#define CATCH_WARN( msg ) (void)(0)
|
|
||||||
#define CATCH_CAPTURE( ... ) (void)(0)
|
|
||||||
|
|
||||||
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
|
|
||||||
|
|
||||||
#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
|
|
||||||
#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
|
|
||||||
#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
|
|
||||||
#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ )
|
|
||||||
|
|
||||||
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
|
|
||||||
|
|
||||||
#define INFO( msg ) (void)(0)
|
|
||||||
#define UNSCOPED_INFO( msg ) (void)(0)
|
|
||||||
#define WARN( msg ) (void)(0)
|
|
||||||
#define CAPTURE( ... ) (void)(0)
|
|
||||||
|
|
||||||
#endif // end of user facing macro declarations
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // CATCH_MESSAGE_HPP_INCLUDED
|
|
@ -1,107 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_context.hpp>
|
|
||||||
#include <catch2/internal/catch_enforce.hpp>
|
|
||||||
#include <catch2/internal/catch_test_case_registry_impl.hpp>
|
|
||||||
#include <catch2/internal/catch_reporter_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_exception_translator_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_tag_alias_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_startup_exception_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_singletons.hpp>
|
|
||||||
#include <catch2/internal/catch_enum_values_registry.hpp>
|
|
||||||
#include <catch2/catch_test_case_info.hpp>
|
|
||||||
#include <catch2/internal/catch_noncopyable.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
#include <catch2/internal/catch_reporter_registry.hpp>
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class RegistryHub : public IRegistryHub,
|
|
||||||
public IMutableRegistryHub,
|
|
||||||
private Detail::NonCopyable {
|
|
||||||
|
|
||||||
public: // IRegistryHub
|
|
||||||
RegistryHub() = default;
|
|
||||||
ReporterRegistry const& getReporterRegistry() const override {
|
|
||||||
return m_reporterRegistry;
|
|
||||||
}
|
|
||||||
ITestCaseRegistry const& getTestCaseRegistry() const override {
|
|
||||||
return m_testCaseRegistry;
|
|
||||||
}
|
|
||||||
IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override {
|
|
||||||
return m_exceptionTranslatorRegistry;
|
|
||||||
}
|
|
||||||
ITagAliasRegistry const& getTagAliasRegistry() const override {
|
|
||||||
return m_tagAliasRegistry;
|
|
||||||
}
|
|
||||||
StartupExceptionRegistry const& getStartupExceptionRegistry() const override {
|
|
||||||
return m_exceptionRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public: // IMutableRegistryHub
|
|
||||||
void registerReporter( std::string const& name, IReporterFactoryPtr factory ) override {
|
|
||||||
m_reporterRegistry.registerReporter( name, CATCH_MOVE(factory) );
|
|
||||||
}
|
|
||||||
void registerListener( Detail::unique_ptr<EventListenerFactory> factory ) override {
|
|
||||||
m_reporterRegistry.registerListener( CATCH_MOVE(factory) );
|
|
||||||
}
|
|
||||||
void registerTest( Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker ) override {
|
|
||||||
m_testCaseRegistry.registerTest( CATCH_MOVE(testInfo), CATCH_MOVE(invoker) );
|
|
||||||
}
|
|
||||||
void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) override {
|
|
||||||
m_exceptionTranslatorRegistry.registerTranslator( CATCH_MOVE(translator) );
|
|
||||||
}
|
|
||||||
void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override {
|
|
||||||
m_tagAliasRegistry.add( alias, tag, lineInfo );
|
|
||||||
}
|
|
||||||
void registerStartupException() noexcept override {
|
|
||||||
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
|
||||||
m_exceptionRegistry.add(std::current_exception());
|
|
||||||
#else
|
|
||||||
CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
|
|
||||||
return m_enumValuesRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
TestRegistry m_testCaseRegistry;
|
|
||||||
ReporterRegistry m_reporterRegistry;
|
|
||||||
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
|
|
||||||
TagAliasRegistry m_tagAliasRegistry;
|
|
||||||
StartupExceptionRegistry m_exceptionRegistry;
|
|
||||||
Detail::EnumValuesRegistry m_enumValuesRegistry;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>;
|
|
||||||
|
|
||||||
IRegistryHub const& getRegistryHub() {
|
|
||||||
return RegistryHubSingleton::get();
|
|
||||||
}
|
|
||||||
IMutableRegistryHub& getMutableRegistryHub() {
|
|
||||||
return RegistryHubSingleton::getMutable();
|
|
||||||
}
|
|
||||||
void cleanUp() {
|
|
||||||
cleanupSingletons();
|
|
||||||
cleanUpContext();
|
|
||||||
}
|
|
||||||
std::string translateActiveException() {
|
|
||||||
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
@ -1,42 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_SECTION_INFO_HPP_INCLUDED
|
|
||||||
#define CATCH_SECTION_INFO_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
#include <catch2/internal/catch_source_line_info.hpp>
|
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
|
||||||
#include <catch2/catch_totals.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
struct SectionInfo {
|
|
||||||
// The last argument is ignored, so that people can write
|
|
||||||
// SECTION("ShortName", "Proper description that is long") and
|
|
||||||
// still use the `-c` flag comfortably.
|
|
||||||
SectionInfo( SourceLineInfo const& _lineInfo, std::string _name,
|
|
||||||
const char* const = nullptr ):
|
|
||||||
name(CATCH_MOVE(_name)),
|
|
||||||
lineInfo(_lineInfo)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
SourceLineInfo lineInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SectionEndInfo {
|
|
||||||
SectionInfo sectionInfo;
|
|
||||||
Counts prevAssertions;
|
|
||||||
double durationInSeconds;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_SECTION_INFO_HPP_INCLUDED
|
|
@ -1,364 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#include <catch2/catch_session.hpp>
|
|
||||||
#include <catch2/internal/catch_console_colour.hpp>
|
|
||||||
#include <catch2/internal/catch_enforce.hpp>
|
|
||||||
#include <catch2/internal/catch_list.hpp>
|
|
||||||
#include <catch2/internal/catch_context.hpp>
|
|
||||||
#include <catch2/internal/catch_run_context.hpp>
|
|
||||||
#include <catch2/catch_test_spec.hpp>
|
|
||||||
#include <catch2/catch_version.hpp>
|
|
||||||
#include <catch2/internal/catch_startup_exception_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_sharding.hpp>
|
|
||||||
#include <catch2/internal/catch_test_case_registry_impl.hpp>
|
|
||||||
#include <catch2/internal/catch_textflow.hpp>
|
|
||||||
#include <catch2/internal/catch_windows_h_proxy.hpp>
|
|
||||||
#include <catch2/reporters/catch_reporter_multi.hpp>
|
|
||||||
#include <catch2/internal/catch_reporter_registry.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
|
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
||||||
#include <catch2/internal/catch_stdstreams.hpp>
|
|
||||||
#include <catch2/internal/catch_istream.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include <exception>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
const int MaxExitCode = 255;
|
|
||||||
|
|
||||||
IEventListenerPtr createReporter(std::string const& reporterName, ReporterConfig&& config) {
|
|
||||||
auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config));
|
|
||||||
CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << '\'');
|
|
||||||
|
|
||||||
return reporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
IEventListenerPtr prepareReporters(Config const* config) {
|
|
||||||
if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()
|
|
||||||
&& config->getProcessedReporterSpecs().size() == 1) {
|
|
||||||
auto const& spec = config->getProcessedReporterSpecs()[0];
|
|
||||||
return createReporter(
|
|
||||||
spec.name,
|
|
||||||
ReporterConfig( config,
|
|
||||||
makeStream( spec.outputFilename ),
|
|
||||||
spec.colourMode,
|
|
||||||
spec.customOptions ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto multi = Detail::make_unique<MultiReporter>(config);
|
|
||||||
|
|
||||||
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
|
|
||||||
for (auto const& listener : listeners) {
|
|
||||||
multi->addListener(listener->create(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( auto const& reporterSpec : config->getProcessedReporterSpecs() ) {
|
|
||||||
multi->addReporter( createReporter(
|
|
||||||
reporterSpec.name,
|
|
||||||
ReporterConfig( config,
|
|
||||||
makeStream( reporterSpec.outputFilename ),
|
|
||||||
reporterSpec.colourMode,
|
|
||||||
reporterSpec.customOptions ) ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return multi;
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestGroup {
|
|
||||||
public:
|
|
||||||
explicit TestGroup(IEventListenerPtr&& reporter, Config const* config):
|
|
||||||
m_reporter(reporter.get()),
|
|
||||||
m_config{config},
|
|
||||||
m_context{config, CATCH_MOVE(reporter)} {
|
|
||||||
|
|
||||||
assert( m_config->testSpec().getInvalidSpecs().empty() &&
|
|
||||||
"Invalid test specs should be handled before running tests" );
|
|
||||||
|
|
||||||
auto const& allTestCases = getAllTestCasesSorted(*m_config);
|
|
||||||
auto const& testSpec = m_config->testSpec();
|
|
||||||
if ( !testSpec.hasFilters() ) {
|
|
||||||
for ( auto const& test : allTestCases ) {
|
|
||||||
if ( !test.getTestCaseInfo().isHidden() ) {
|
|
||||||
m_tests.emplace( &test );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_matches =
|
|
||||||
testSpec.matchesByFilter( allTestCases, *m_config );
|
|
||||||
for ( auto const& match : m_matches ) {
|
|
||||||
m_tests.insert( match.tests.begin(),
|
|
||||||
match.tests.end() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tests = createShard(m_tests, m_config->shardCount(), m_config->shardIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
Totals execute() {
|
|
||||||
Totals totals;
|
|
||||||
for (auto const& testCase : m_tests) {
|
|
||||||
if (!m_context.aborting())
|
|
||||||
totals += m_context.runTest(*testCase);
|
|
||||||
else
|
|
||||||
m_reporter->skipTest(testCase->getTestCaseInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& match : m_matches) {
|
|
||||||
if (match.tests.empty()) {
|
|
||||||
m_unmatchedTestSpecs = true;
|
|
||||||
m_reporter->noMatchingTestCases( match.name );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totals;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hadUnmatchedTestSpecs() const {
|
|
||||||
return m_unmatchedTestSpecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
IEventListener* m_reporter;
|
|
||||||
Config const* m_config;
|
|
||||||
RunContext m_context;
|
|
||||||
std::set<TestCaseHandle const*> m_tests;
|
|
||||||
TestSpec::Matches m_matches;
|
|
||||||
bool m_unmatchedTestSpecs = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
void applyFilenamesAsTags() {
|
|
||||||
for (auto const& testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) {
|
|
||||||
testInfo->addFilenameTag();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anon namespace
|
|
||||||
|
|
||||||
Session::Session() {
|
|
||||||
static bool alreadyInstantiated = false;
|
|
||||||
if( alreadyInstantiated ) {
|
|
||||||
CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
|
|
||||||
CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// There cannot be exceptions at startup in no-exception mode.
|
|
||||||
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
|
||||||
const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
|
|
||||||
if ( !exceptions.empty() ) {
|
|
||||||
config();
|
|
||||||
getCurrentMutableContext().setConfig(m_config.get());
|
|
||||||
|
|
||||||
m_startupExceptions = true;
|
|
||||||
auto errStream = makeStream( "%stderr" );
|
|
||||||
auto colourImpl = makeColourImpl(
|
|
||||||
ColourMode::PlatformDefault, errStream.get() );
|
|
||||||
auto guard = colourImpl->guardColour( Colour::Red );
|
|
||||||
errStream->stream() << "Errors occurred during startup!" << '\n';
|
|
||||||
// iterate over all exceptions and notify user
|
|
||||||
for ( const auto& ex_ptr : exceptions ) {
|
|
||||||
try {
|
|
||||||
std::rethrow_exception(ex_ptr);
|
|
||||||
} catch ( std::exception const& ex ) {
|
|
||||||
errStream->stream() << TextFlow::Column( ex.what() ).indent(2) << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
alreadyInstantiated = true;
|
|
||||||
m_cli = makeCommandLineParser( m_configData );
|
|
||||||
}
|
|
||||||
Session::~Session() {
|
|
||||||
Catch::cleanUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Session::showHelp() const {
|
|
||||||
Catch::cout()
|
|
||||||
<< "\nCatch2 v" << libraryVersion() << '\n'
|
|
||||||
<< m_cli << '\n'
|
|
||||||
<< "For more detailed usage please see the project docs\n\n" << std::flush;
|
|
||||||
}
|
|
||||||
void Session::libIdentify() {
|
|
||||||
Catch::cout()
|
|
||||||
<< std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
|
|
||||||
<< std::left << std::setw(16) << "category: " << "testframework\n"
|
|
||||||
<< std::left << std::setw(16) << "framework: " << "Catch2\n"
|
|
||||||
<< std::left << std::setw(16) << "version: " << libraryVersion() << '\n' << std::flush;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Session::applyCommandLine( int argc, char const * const * argv ) {
|
|
||||||
if( m_startupExceptions )
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
auto result = m_cli.parse( Clara::Args( argc, argv ) );
|
|
||||||
|
|
||||||
if( !result ) {
|
|
||||||
config();
|
|
||||||
getCurrentMutableContext().setConfig(m_config.get());
|
|
||||||
auto errStream = makeStream( "%stderr" );
|
|
||||||
auto colour = makeColourImpl( ColourMode::PlatformDefault, errStream.get() );
|
|
||||||
|
|
||||||
errStream->stream()
|
|
||||||
<< colour->guardColour( Colour::Red )
|
|
||||||
<< "\nError(s) in input:\n"
|
|
||||||
<< TextFlow::Column( result.errorMessage() ).indent( 2 )
|
|
||||||
<< "\n\n";
|
|
||||||
errStream->stream() << "Run with -? for usage\n\n" << std::flush;
|
|
||||||
return MaxExitCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( m_configData.showHelp )
|
|
||||||
showHelp();
|
|
||||||
if( m_configData.libIdentify )
|
|
||||||
libIdentify();
|
|
||||||
|
|
||||||
m_config.reset();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
|
|
||||||
int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
|
|
||||||
|
|
||||||
char **utf8Argv = new char *[ argc ];
|
|
||||||
|
|
||||||
for ( int i = 0; i < argc; ++i ) {
|
|
||||||
int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr );
|
|
||||||
|
|
||||||
utf8Argv[ i ] = new char[ bufSize ];
|
|
||||||
|
|
||||||
WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr );
|
|
||||||
}
|
|
||||||
|
|
||||||
int returnCode = applyCommandLine( argc, utf8Argv );
|
|
||||||
|
|
||||||
for ( int i = 0; i < argc; ++i )
|
|
||||||
delete [] utf8Argv[ i ];
|
|
||||||
|
|
||||||
delete [] utf8Argv;
|
|
||||||
|
|
||||||
return returnCode;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Session::useConfigData( ConfigData const& configData ) {
|
|
||||||
m_configData = configData;
|
|
||||||
m_config.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
int Session::run() {
|
|
||||||
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
|
|
||||||
Catch::cout() << "...waiting for enter/ return before starting\n" << std::flush;
|
|
||||||
static_cast<void>(std::getchar());
|
|
||||||
}
|
|
||||||
int exitCode = runInternal();
|
|
||||||
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
|
|
||||||
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
|
|
||||||
static_cast<void>(std::getchar());
|
|
||||||
}
|
|
||||||
return exitCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clara::Parser const& Session::cli() const {
|
|
||||||
return m_cli;
|
|
||||||
}
|
|
||||||
void Session::cli( Clara::Parser const& newParser ) {
|
|
||||||
m_cli = newParser;
|
|
||||||
}
|
|
||||||
ConfigData& Session::configData() {
|
|
||||||
return m_configData;
|
|
||||||
}
|
|
||||||
Config& Session::config() {
|
|
||||||
if( !m_config )
|
|
||||||
m_config = Detail::make_unique<Config>( m_configData );
|
|
||||||
return *m_config;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Session::runInternal() {
|
|
||||||
if( m_startupExceptions )
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (m_configData.showHelp || m_configData.libIdentify) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( m_configData.shardIndex >= m_configData.shardCount ) {
|
|
||||||
Catch::cerr() << "The shard count (" << m_configData.shardCount
|
|
||||||
<< ") must be greater than the shard index ("
|
|
||||||
<< m_configData.shardIndex << ")\n"
|
|
||||||
<< std::flush;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
CATCH_TRY {
|
|
||||||
config(); // Force config to be constructed
|
|
||||||
|
|
||||||
seedRng( *m_config );
|
|
||||||
|
|
||||||
if (m_configData.filenamesAsTags) {
|
|
||||||
applyFilenamesAsTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up global config instance before we start calling into other functions
|
|
||||||
getCurrentMutableContext().setConfig(m_config.get());
|
|
||||||
|
|
||||||
// Create reporter(s) so we can route listings through them
|
|
||||||
auto reporter = prepareReporters(m_config.get());
|
|
||||||
|
|
||||||
auto const& invalidSpecs = m_config->testSpec().getInvalidSpecs();
|
|
||||||
if ( !invalidSpecs.empty() ) {
|
|
||||||
for ( auto const& spec : invalidSpecs ) {
|
|
||||||
reporter->reportInvalidTestSpec( spec );
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Handle list request
|
|
||||||
if (list(*reporter, *m_config)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
|
|
||||||
auto const totals = tests.execute();
|
|
||||||
|
|
||||||
if ( tests.hadUnmatchedTestSpecs()
|
|
||||||
&& m_config->warnAboutUnmatchedTestSpecs() ) {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( totals.testCases.total() == 0
|
|
||||||
&& !m_config->zeroTestsCountAsSuccess() ) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( totals.testCases.total() > 0 &&
|
|
||||||
totals.testCases.total() == totals.testCases.skipped
|
|
||||||
&& !m_config->zeroTestsCountAsSuccess() ) {
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that on unices only the lower 8 bits are usually used, clamping
|
|
||||||
// the return value to 255 prevents false negative when some multiple
|
|
||||||
// of 256 tests has failed
|
|
||||||
return (std::min) (MaxExitCode, static_cast<int>(totals.assertions.failed));
|
|
||||||
}
|
|
||||||
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
|
||||||
catch( std::exception& ex ) {
|
|
||||||
Catch::cerr() << ex.what() << '\n' << std::flush;
|
|
||||||
return MaxExitCode;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
@ -1,62 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_SESSION_HPP_INCLUDED
|
|
||||||
#define CATCH_SESSION_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_commandline.hpp>
|
|
||||||
#include <catch2/internal/catch_noncopyable.hpp>
|
|
||||||
#include <catch2/catch_config.hpp>
|
|
||||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
|
||||||
#include <catch2/internal/catch_config_wchar.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
class Session : Detail::NonCopyable {
|
|
||||||
public:
|
|
||||||
|
|
||||||
Session();
|
|
||||||
~Session();
|
|
||||||
|
|
||||||
void showHelp() const;
|
|
||||||
void libIdentify();
|
|
||||||
|
|
||||||
int applyCommandLine( int argc, char const * const * argv );
|
|
||||||
#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
|
|
||||||
int applyCommandLine( int argc, wchar_t const * const * argv );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void useConfigData( ConfigData const& configData );
|
|
||||||
|
|
||||||
template<typename CharT>
|
|
||||||
int run(int argc, CharT const * const argv[]) {
|
|
||||||
if (m_startupExceptions)
|
|
||||||
return 1;
|
|
||||||
int returnCode = applyCommandLine(argc, argv);
|
|
||||||
if (returnCode == 0)
|
|
||||||
returnCode = run();
|
|
||||||
return returnCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
int run();
|
|
||||||
|
|
||||||
Clara::Parser const& cli() const;
|
|
||||||
void cli( Clara::Parser const& newParser );
|
|
||||||
ConfigData& configData();
|
|
||||||
Config& config();
|
|
||||||
private:
|
|
||||||
int runInternal();
|
|
||||||
|
|
||||||
Clara::Parser m_cli;
|
|
||||||
ConfigData m_configData;
|
|
||||||
Detail::unique_ptr<Config> m_config;
|
|
||||||
bool m_startupExceptions = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_SESSION_HPP_INCLUDED
|
|
@ -1,29 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_TAG_ALIAS_HPP_INCLUDED
|
|
||||||
#define CATCH_TAG_ALIAS_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_source_line_info.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
struct TagAlias {
|
|
||||||
TagAlias(std::string const& _tag, SourceLineInfo _lineInfo):
|
|
||||||
tag(_tag),
|
|
||||||
lineInfo(_lineInfo)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::string tag;
|
|
||||||
SourceLineInfo lineInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
#endif // CATCH_TAG_ALIAS_HPP_INCLUDED
|
|
@ -1,24 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
|
|
||||||
#include <catch2/catch_tag_alias_autoregistrar.hpp>
|
|
||||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) {
|
|
||||||
CATCH_TRY {
|
|
||||||
getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo);
|
|
||||||
} CATCH_CATCH_ALL {
|
|
||||||
// Do not throw when constructing global objects, instead register the exception to be processed later
|
|
||||||
getMutableRegistryHub().registerStartupException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
|
|
||||||
#define CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
|
||||||
#include <catch2/internal/catch_unique_name.hpp>
|
|
||||||
#include <catch2/internal/catch_source_line_info.hpp>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
struct RegistrarForTagAliases {
|
|
||||||
RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
||||||
|
|
||||||
#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
|
|
||||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
|
||||||
namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
|
|
||||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
|
||||||
|
|
||||||
#endif // CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
|
|
@ -1,124 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED
|
|
||||||
#define CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED
|
|
||||||
|
|
||||||
// We need this suppression to leak, because it took until GCC 10
|
|
||||||
// for the front end to handle local suppression via _Pragma properly
|
|
||||||
// inside templates (so `TEMPLATE_TEST_CASE` and co).
|
|
||||||
// **THIS IS DIFFERENT FOR STANDARD TESTS, WHERE GCC 9 IS SUFFICIENT**
|
|
||||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ < 10
|
|
||||||
#pragma GCC diagnostic ignored "-Wparentheses"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include <catch2/catch_test_macros.hpp>
|
|
||||||
#include <catch2/internal/catch_template_test_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_preprocessor.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
|
|
||||||
|
|
||||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
|
|
||||||
#define CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#else
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
|
||||||
#define CATCH_TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
|
|
||||||
#define CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
|
|
||||||
|
|
||||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
|
|
||||||
#else
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
|
|
||||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// When disabled, these can be shared between proper preprocessor and MSVC preprocessor
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#define CATCH_TEMPLATE_LIST_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__)
|
|
||||||
#define CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
|
|
||||||
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
|
|
||||||
|
|
||||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
|
||||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
|
|
||||||
#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#else
|
|
||||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
|
|
||||||
#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
|
|
||||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
|
||||||
#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
|
||||||
#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
|
|
||||||
#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
|
|
||||||
|
|
||||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
|
||||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
|
|
||||||
#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
|
|
||||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
|
|
||||||
#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
|
|
||||||
#else
|
|
||||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
|
|
||||||
#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
|
|
||||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
|
|
||||||
#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// When disabled, these can be shared between proper preprocessor and MSVC preprocessor
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#define TEMPLATE_LIST_TEST_CASE( ... ) TEMPLATE_TEST_CASE(__VA_ARGS__)
|
|
||||||
#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
|
|
||||||
#endif // end of user facing macro declarations
|
|
||||||
|
|
||||||
|
|
||||||
#endif // CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED
|
|
@ -1,263 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#include <catch2/catch_test_case_info.hpp>
|
|
||||||
#include <catch2/internal/catch_enforce.hpp>
|
|
||||||
#include <catch2/internal/catch_string_manip.hpp>
|
|
||||||
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cctype>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
using TCP_underlying_type = uint8_t;
|
|
||||||
static_assert(sizeof(TestCaseProperties) == sizeof(TCP_underlying_type),
|
|
||||||
"The size of the TestCaseProperties is different from the assumed size");
|
|
||||||
|
|
||||||
TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) {
|
|
||||||
return static_cast<TestCaseProperties>(
|
|
||||||
static_cast<TCP_underlying_type>(lhs) | static_cast<TCP_underlying_type>(rhs)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) {
|
|
||||||
lhs = static_cast<TestCaseProperties>(
|
|
||||||
static_cast<TCP_underlying_type>(lhs) | static_cast<TCP_underlying_type>(rhs)
|
|
||||||
);
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) {
|
|
||||||
return static_cast<TestCaseProperties>(
|
|
||||||
static_cast<TCP_underlying_type>(lhs) & static_cast<TCP_underlying_type>(rhs)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool applies(TestCaseProperties tcp) {
|
|
||||||
static_assert(static_cast<TCP_underlying_type>(TestCaseProperties::None) == 0,
|
|
||||||
"TestCaseProperties::None must be equal to 0");
|
|
||||||
return tcp != TestCaseProperties::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestCaseProperties parseSpecialTag( StringRef tag ) {
|
|
||||||
if( !tag.empty() && tag[0] == '.' )
|
|
||||||
return TestCaseProperties::IsHidden;
|
|
||||||
else if( tag == "!throws"_sr )
|
|
||||||
return TestCaseProperties::Throws;
|
|
||||||
else if( tag == "!shouldfail"_sr )
|
|
||||||
return TestCaseProperties::ShouldFail;
|
|
||||||
else if( tag == "!mayfail"_sr )
|
|
||||||
return TestCaseProperties::MayFail;
|
|
||||||
else if( tag == "!nonportable"_sr )
|
|
||||||
return TestCaseProperties::NonPortable;
|
|
||||||
else if( tag == "!benchmark"_sr )
|
|
||||||
return TestCaseProperties::Benchmark | TestCaseProperties::IsHidden;
|
|
||||||
else
|
|
||||||
return TestCaseProperties::None;
|
|
||||||
}
|
|
||||||
bool isReservedTag( StringRef tag ) {
|
|
||||||
return parseSpecialTag( tag ) == TestCaseProperties::None
|
|
||||||
&& tag.size() > 0
|
|
||||||
&& !std::isalnum( static_cast<unsigned char>(tag[0]) );
|
|
||||||
}
|
|
||||||
void enforceNotReservedTag( StringRef tag, SourceLineInfo const& _lineInfo ) {
|
|
||||||
CATCH_ENFORCE( !isReservedTag(tag),
|
|
||||||
"Tag name: [" << tag << "] is not allowed.\n"
|
|
||||||
<< "Tag names starting with non alphanumeric characters are reserved\n"
|
|
||||||
<< _lineInfo );
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string makeDefaultName() {
|
|
||||||
static size_t counter = 0;
|
|
||||||
return "Anonymous test case " + std::to_string(++counter);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringRef extractFilenamePart(StringRef filename) {
|
|
||||||
size_t lastDot = filename.size();
|
|
||||||
while (lastDot > 0 && filename[lastDot - 1] != '.') {
|
|
||||||
--lastDot;
|
|
||||||
}
|
|
||||||
--lastDot;
|
|
||||||
|
|
||||||
size_t nameStart = lastDot;
|
|
||||||
while (nameStart > 0 && filename[nameStart - 1] != '/' && filename[nameStart - 1] != '\\') {
|
|
||||||
--nameStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
return filename.substr(nameStart, lastDot - nameStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the upper bound on size of extra tags ([#file]+[.])
|
|
||||||
size_t sizeOfExtraTags(StringRef filepath) {
|
|
||||||
// [.] is 3, [#] is another 3
|
|
||||||
const size_t extras = 3 + 3;
|
|
||||||
return extractFilenamePart(filepath).size() + extras;
|
|
||||||
}
|
|
||||||
} // end unnamed namespace
|
|
||||||
|
|
||||||
bool operator<( Tag const& lhs, Tag const& rhs ) {
|
|
||||||
Detail::CaseInsensitiveLess cmp;
|
|
||||||
return cmp( lhs.original, rhs.original );
|
|
||||||
}
|
|
||||||
bool operator==( Tag const& lhs, Tag const& rhs ) {
|
|
||||||
Detail::CaseInsensitiveEqualTo cmp;
|
|
||||||
return cmp( lhs.original, rhs.original );
|
|
||||||
}
|
|
||||||
|
|
||||||
Detail::unique_ptr<TestCaseInfo>
|
|
||||||
makeTestCaseInfo(StringRef _className,
|
|
||||||
NameAndTags const& nameAndTags,
|
|
||||||
SourceLineInfo const& _lineInfo ) {
|
|
||||||
return Detail::make_unique<TestCaseInfo>(_className, nameAndTags, _lineInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
TestCaseInfo::TestCaseInfo(StringRef _className,
|
|
||||||
NameAndTags const& _nameAndTags,
|
|
||||||
SourceLineInfo const& _lineInfo):
|
|
||||||
name( _nameAndTags.name.empty() ? makeDefaultName() : _nameAndTags.name ),
|
|
||||||
className( _className ),
|
|
||||||
lineInfo( _lineInfo )
|
|
||||||
{
|
|
||||||
StringRef originalTags = _nameAndTags.tags;
|
|
||||||
// We need to reserve enough space to store all of the tags
|
|
||||||
// (including optional hidden tag and filename tag)
|
|
||||||
auto requiredSize = originalTags.size() + sizeOfExtraTags(_lineInfo.file);
|
|
||||||
backingTags.reserve(requiredSize);
|
|
||||||
|
|
||||||
// We cannot copy the tags directly, as we need to normalize
|
|
||||||
// some tags, so that [.foo] is copied as [.][foo].
|
|
||||||
size_t tagStart = 0;
|
|
||||||
size_t tagEnd = 0;
|
|
||||||
bool inTag = false;
|
|
||||||
for (size_t idx = 0; idx < originalTags.size(); ++idx) {
|
|
||||||
auto c = originalTags[idx];
|
|
||||||
if (c == '[') {
|
|
||||||
CATCH_ENFORCE(
|
|
||||||
!inTag,
|
|
||||||
"Found '[' inside a tag while registering test case '"
|
|
||||||
<< _nameAndTags.name << "' at " << _lineInfo );
|
|
||||||
|
|
||||||
inTag = true;
|
|
||||||
tagStart = idx;
|
|
||||||
}
|
|
||||||
if (c == ']') {
|
|
||||||
CATCH_ENFORCE(
|
|
||||||
inTag,
|
|
||||||
"Found unmatched ']' while registering test case '"
|
|
||||||
<< _nameAndTags.name << "' at " << _lineInfo );
|
|
||||||
|
|
||||||
inTag = false;
|
|
||||||
tagEnd = idx;
|
|
||||||
assert(tagStart < tagEnd);
|
|
||||||
|
|
||||||
// We need to check the tag for special meanings, copy
|
|
||||||
// it over to backing storage and actually reference the
|
|
||||||
// backing storage in the saved tags
|
|
||||||
StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1);
|
|
||||||
CATCH_ENFORCE( !tagStr.empty(),
|
|
||||||
"Found an empty tag while registering test case '"
|
|
||||||
<< _nameAndTags.name << "' at "
|
|
||||||
<< _lineInfo );
|
|
||||||
|
|
||||||
enforceNotReservedTag(tagStr, lineInfo);
|
|
||||||
properties |= parseSpecialTag(tagStr);
|
|
||||||
// When copying a tag to the backing storage, we need to
|
|
||||||
// check if it is a merged hide tag, such as [.foo], and
|
|
||||||
// if it is, we need to handle it as if it was [foo].
|
|
||||||
if (tagStr.size() > 1 && tagStr[0] == '.') {
|
|
||||||
tagStr = tagStr.substr(1, tagStr.size() - 1);
|
|
||||||
}
|
|
||||||
// We skip over dealing with the [.] tag, as we will add
|
|
||||||
// it later unconditionally and then sort and unique all
|
|
||||||
// the tags.
|
|
||||||
internalAppendTag(tagStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CATCH_ENFORCE( !inTag,
|
|
||||||
"Found an unclosed tag while registering test case '"
|
|
||||||
<< _nameAndTags.name << "' at " << _lineInfo );
|
|
||||||
|
|
||||||
|
|
||||||
// Add [.] if relevant
|
|
||||||
if (isHidden()) {
|
|
||||||
internalAppendTag("."_sr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort and prepare tags
|
|
||||||
std::sort(begin(tags), end(tags));
|
|
||||||
tags.erase(std::unique(begin(tags), end(tags)),
|
|
||||||
end(tags));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TestCaseInfo::isHidden() const {
|
|
||||||
return applies( properties & TestCaseProperties::IsHidden );
|
|
||||||
}
|
|
||||||
bool TestCaseInfo::throws() const {
|
|
||||||
return applies( properties & TestCaseProperties::Throws );
|
|
||||||
}
|
|
||||||
bool TestCaseInfo::okToFail() const {
|
|
||||||
return applies( properties & (TestCaseProperties::ShouldFail | TestCaseProperties::MayFail ) );
|
|
||||||
}
|
|
||||||
bool TestCaseInfo::expectedToFail() const {
|
|
||||||
return applies( properties & (TestCaseProperties::ShouldFail) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestCaseInfo::addFilenameTag() {
|
|
||||||
std::string combined("#");
|
|
||||||
combined += extractFilenamePart(lineInfo.file);
|
|
||||||
internalAppendTag(combined);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string TestCaseInfo::tagsAsString() const {
|
|
||||||
std::string ret;
|
|
||||||
// '[' and ']' per tag
|
|
||||||
std::size_t full_size = 2 * tags.size();
|
|
||||||
for (const auto& tag : tags) {
|
|
||||||
full_size += tag.original.size();
|
|
||||||
}
|
|
||||||
ret.reserve(full_size);
|
|
||||||
for (const auto& tag : tags) {
|
|
||||||
ret.push_back('[');
|
|
||||||
ret += tag.original;
|
|
||||||
ret.push_back(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestCaseInfo::internalAppendTag(StringRef tagStr) {
|
|
||||||
backingTags += '[';
|
|
||||||
const auto backingStart = backingTags.size();
|
|
||||||
backingTags += tagStr;
|
|
||||||
const auto backingEnd = backingTags.size();
|
|
||||||
backingTags += ']';
|
|
||||||
tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator<( TestCaseInfo const& lhs, TestCaseInfo const& rhs ) {
|
|
||||||
// We want to avoid redoing the string comparisons multiple times,
|
|
||||||
// so we store the result of a three-way comparison before using
|
|
||||||
// it in the actual comparison logic.
|
|
||||||
const auto cmpName = lhs.name.compare( rhs.name );
|
|
||||||
if ( cmpName != 0 ) {
|
|
||||||
return cmpName < 0;
|
|
||||||
}
|
|
||||||
const auto cmpClassName = lhs.className.compare( rhs.className );
|
|
||||||
if ( cmpClassName != 0 ) {
|
|
||||||
return cmpClassName < 0;
|
|
||||||
}
|
|
||||||
return lhs.tags < rhs.tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestCaseInfo const& TestCaseHandle::getTestCaseInfo() const {
|
|
||||||
return *m_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end namespace Catch
|
|
@ -1,131 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_TEST_CASE_INFO_HPP_INCLUDED
|
|
||||||
#define CATCH_TEST_CASE_INFO_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_source_line_info.hpp>
|
|
||||||
#include <catch2/internal/catch_noncopyable.hpp>
|
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
|
||||||
#include <catch2/internal/catch_test_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#ifdef __clang__
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpadded"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A **view** of a tag string that provides case insensitive comparisons
|
|
||||||
*
|
|
||||||
* Note that in Catch2 internals, the square brackets around tags are
|
|
||||||
* not a part of tag's representation, so e.g. "[cool-tag]" is represented
|
|
||||||
* as "cool-tag" internally.
|
|
||||||
*/
|
|
||||||
struct Tag {
|
|
||||||
constexpr Tag(StringRef original_):
|
|
||||||
original(original_)
|
|
||||||
{}
|
|
||||||
StringRef original;
|
|
||||||
|
|
||||||
friend bool operator< ( Tag const& lhs, Tag const& rhs );
|
|
||||||
friend bool operator==( Tag const& lhs, Tag const& rhs );
|
|
||||||
};
|
|
||||||
|
|
||||||
class ITestInvoker;
|
|
||||||
|
|
||||||
enum class TestCaseProperties : uint8_t {
|
|
||||||
None = 0,
|
|
||||||
IsHidden = 1 << 1,
|
|
||||||
ShouldFail = 1 << 2,
|
|
||||||
MayFail = 1 << 3,
|
|
||||||
Throws = 1 << 4,
|
|
||||||
NonPortable = 1 << 5,
|
|
||||||
Benchmark = 1 << 6
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Various metadata about the test case.
|
|
||||||
*
|
|
||||||
* A test case is uniquely identified by its (class)name and tags
|
|
||||||
* combination, with source location being ignored, and other properties
|
|
||||||
* being determined from tags.
|
|
||||||
*
|
|
||||||
* Tags are kept sorted.
|
|
||||||
*/
|
|
||||||
struct TestCaseInfo : Detail::NonCopyable {
|
|
||||||
|
|
||||||
TestCaseInfo(StringRef _className,
|
|
||||||
NameAndTags const& _tags,
|
|
||||||
SourceLineInfo const& _lineInfo);
|
|
||||||
|
|
||||||
bool isHidden() const;
|
|
||||||
bool throws() const;
|
|
||||||
bool okToFail() const;
|
|
||||||
bool expectedToFail() const;
|
|
||||||
|
|
||||||
// Adds the tag(s) with test's filename (for the -# flag)
|
|
||||||
void addFilenameTag();
|
|
||||||
|
|
||||||
//! Orders by name, classname and tags
|
|
||||||
friend bool operator<( TestCaseInfo const& lhs,
|
|
||||||
TestCaseInfo const& rhs );
|
|
||||||
|
|
||||||
|
|
||||||
std::string tagsAsString() const;
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
StringRef className;
|
|
||||||
private:
|
|
||||||
std::string backingTags;
|
|
||||||
// Internally we copy tags to the backing storage and then add
|
|
||||||
// refs to this storage to the tags vector.
|
|
||||||
void internalAppendTag(StringRef tagString);
|
|
||||||
public:
|
|
||||||
std::vector<Tag> tags;
|
|
||||||
SourceLineInfo lineInfo;
|
|
||||||
TestCaseProperties properties = TestCaseProperties::None;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper over the test case information and the test case invoker
|
|
||||||
*
|
|
||||||
* Does not own either, and is specifically made to be cheap
|
|
||||||
* to copy around.
|
|
||||||
*/
|
|
||||||
class TestCaseHandle {
|
|
||||||
TestCaseInfo* m_info;
|
|
||||||
ITestInvoker* m_invoker;
|
|
||||||
public:
|
|
||||||
TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) :
|
|
||||||
m_info(info), m_invoker(invoker) {}
|
|
||||||
|
|
||||||
void invoke() const {
|
|
||||||
m_invoker->invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
TestCaseInfo const& getTestCaseInfo() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
Detail::unique_ptr<TestCaseInfo>
|
|
||||||
makeTestCaseInfo( StringRef className,
|
|
||||||
NameAndTags const& nameAndTags,
|
|
||||||
SourceLineInfo const& lineInfo );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __clang__
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // CATCH_TEST_CASE_INFO_HPP_INCLUDED
|
|
@ -1,226 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_TEST_MACROS_HPP_INCLUDED
|
|
||||||
#define CATCH_TEST_MACROS_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_test_macro_impl.hpp>
|
|
||||||
#include <catch2/catch_message.hpp>
|
|
||||||
#include <catch2/catch_user_config.hpp>
|
|
||||||
#include <catch2/internal/catch_section.hpp>
|
|
||||||
#include <catch2/internal/catch_test_registry.hpp>
|
|
||||||
#include <catch2/internal/catch_unique_name.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
// All of our user-facing macros support configuration toggle, that
|
|
||||||
// forces them to be defined prefixed with CATCH_. We also like to
|
|
||||||
// support another toggle that can minimize (disable) their implementation.
|
|
||||||
// Given this, we have 4 different configuration options below
|
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
|
|
||||||
|
|
||||||
#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
||||||
#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
|
|
||||||
|
|
||||||
#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
||||||
#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
|
|
||||||
#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
||||||
|
|
||||||
#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
|
|
||||||
#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
|
|
||||||
#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
|
|
||||||
#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
|
|
||||||
#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
|
|
||||||
|
|
||||||
#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
|
|
||||||
#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
|
|
||||||
#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
|
|
||||||
|
|
||||||
#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
|
|
||||||
#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
|
|
||||||
#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
|
|
||||||
#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
|
|
||||||
#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
|
|
||||||
#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
||||||
#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
|
|
||||||
#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
|
|
||||||
#define CATCH_SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
||||||
|
|
||||||
|
|
||||||
#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
|
|
||||||
#define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ )
|
|
||||||
#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ )
|
|
||||||
#define CATCH_STATIC_CHECK( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ )
|
|
||||||
#define CATCH_STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ )
|
|
||||||
#else
|
|
||||||
#define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ )
|
|
||||||
#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ )
|
|
||||||
#define CATCH_STATIC_CHECK( ... ) CATCH_CHECK( __VA_ARGS__ )
|
|
||||||
#define CATCH_STATIC_CHECK_FALSE( ... ) CATCH_CHECK_FALSE( __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// "BDD-style" convenience wrappers
|
|
||||||
#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
|
|
||||||
#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
|
|
||||||
#define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc )
|
|
||||||
#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
|
|
||||||
#define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc )
|
|
||||||
#define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
|
|
||||||
#define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc )
|
|
||||||
#define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc )
|
|
||||||
|
|
||||||
#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, implemented | vv prefixed, disabled
|
|
||||||
|
|
||||||
#define CATCH_REQUIRE( ... ) (void)(0)
|
|
||||||
#define CATCH_REQUIRE_FALSE( ... ) (void)(0)
|
|
||||||
|
|
||||||
#define CATCH_REQUIRE_THROWS( ... ) (void)(0)
|
|
||||||
#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
|
|
||||||
#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0)
|
|
||||||
|
|
||||||
#define CATCH_CHECK( ... ) (void)(0)
|
|
||||||
#define CATCH_CHECK_FALSE( ... ) (void)(0)
|
|
||||||
#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__)
|
|
||||||
#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__))
|
|
||||||
#define CATCH_CHECK_NOFAIL( ... ) (void)(0)
|
|
||||||
|
|
||||||
#define CATCH_CHECK_THROWS( ... ) (void)(0)
|
|
||||||
#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
|
|
||||||
#define CATCH_CHECK_NOTHROW( ... ) (void)(0)
|
|
||||||
|
|
||||||
#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
|
|
||||||
#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
|
|
||||||
#define CATCH_METHOD_AS_TEST_CASE( method, ... )
|
|
||||||
#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
|
|
||||||
#define CATCH_SECTION( ... )
|
|
||||||
#define CATCH_DYNAMIC_SECTION( ... )
|
|
||||||
#define CATCH_FAIL( ... ) (void)(0)
|
|
||||||
#define CATCH_FAIL_CHECK( ... ) (void)(0)
|
|
||||||
#define CATCH_SUCCEED( ... ) (void)(0)
|
|
||||||
#define CATCH_SKIP( ... ) (void)(0)
|
|
||||||
|
|
||||||
#define CATCH_STATIC_REQUIRE( ... ) (void)(0)
|
|
||||||
#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0)
|
|
||||||
#define CATCH_STATIC_CHECK( ... ) (void)(0)
|
|
||||||
#define CATCH_STATIC_CHECK_FALSE( ... ) (void)(0)
|
|
||||||
|
|
||||||
// "BDD-style" convenience wrappers
|
|
||||||
#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
|
|
||||||
#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className )
|
|
||||||
#define CATCH_GIVEN( desc )
|
|
||||||
#define CATCH_AND_GIVEN( desc )
|
|
||||||
#define CATCH_WHEN( desc )
|
|
||||||
#define CATCH_AND_WHEN( desc )
|
|
||||||
#define CATCH_THEN( desc )
|
|
||||||
#define CATCH_AND_THEN( desc )
|
|
||||||
|
|
||||||
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, disabled | vv unprefixed, implemented
|
|
||||||
|
|
||||||
#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
||||||
#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
|
|
||||||
|
|
||||||
#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
||||||
#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
|
|
||||||
#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
||||||
|
|
||||||
#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
|
|
||||||
#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
|
|
||||||
#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
|
|
||||||
#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
|
|
||||||
#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
|
|
||||||
|
|
||||||
#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
|
|
||||||
#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
|
|
||||||
#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
|
|
||||||
|
|
||||||
#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
|
|
||||||
#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
|
||||||
#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
|
|
||||||
#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
|
|
||||||
#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
|
|
||||||
#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
|
|
||||||
#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
||||||
#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
|
|
||||||
#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
|
|
||||||
#define SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ )
|
|
||||||
|
|
||||||
|
|
||||||
#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
|
|
||||||
#define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
|
|
||||||
#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" )
|
|
||||||
#define STATIC_CHECK( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
|
|
||||||
#define STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" )
|
|
||||||
#else
|
|
||||||
#define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ )
|
|
||||||
#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ )
|
|
||||||
#define STATIC_CHECK( ... ) CHECK( __VA_ARGS__ )
|
|
||||||
#define STATIC_CHECK_FALSE( ... ) CHECK_FALSE( __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// "BDD-style" convenience wrappers
|
|
||||||
#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
|
|
||||||
#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
|
|
||||||
#define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc )
|
|
||||||
#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
|
|
||||||
#define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc )
|
|
||||||
#define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
|
|
||||||
#define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc )
|
|
||||||
#define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc )
|
|
||||||
|
|
||||||
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ unprefixed, implemented | vv unprefixed, disabled
|
|
||||||
|
|
||||||
#define REQUIRE( ... ) (void)(0)
|
|
||||||
#define REQUIRE_FALSE( ... ) (void)(0)
|
|
||||||
|
|
||||||
#define REQUIRE_THROWS( ... ) (void)(0)
|
|
||||||
#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
|
|
||||||
#define REQUIRE_NOTHROW( ... ) (void)(0)
|
|
||||||
|
|
||||||
#define CHECK( ... ) (void)(0)
|
|
||||||
#define CHECK_FALSE( ... ) (void)(0)
|
|
||||||
#define CHECKED_IF( ... ) if (__VA_ARGS__)
|
|
||||||
#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__))
|
|
||||||
#define CHECK_NOFAIL( ... ) (void)(0)
|
|
||||||
|
|
||||||
#define CHECK_THROWS( ... ) (void)(0)
|
|
||||||
#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
|
|
||||||
#define CHECK_NOTHROW( ... ) (void)(0)
|
|
||||||
|
|
||||||
#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__)
|
|
||||||
#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
|
|
||||||
#define METHOD_AS_TEST_CASE( method, ... )
|
|
||||||
#define REGISTER_TEST_CASE( Function, ... ) (void)(0)
|
|
||||||
#define SECTION( ... )
|
|
||||||
#define DYNAMIC_SECTION( ... )
|
|
||||||
#define FAIL( ... ) (void)(0)
|
|
||||||
#define FAIL_CHECK( ... ) (void)(0)
|
|
||||||
#define SUCCEED( ... ) (void)(0)
|
|
||||||
#define SKIP( ... ) (void)(0)
|
|
||||||
|
|
||||||
#define STATIC_REQUIRE( ... ) (void)(0)
|
|
||||||
#define STATIC_REQUIRE_FALSE( ... ) (void)(0)
|
|
||||||
#define STATIC_CHECK( ... ) (void)(0)
|
|
||||||
#define STATIC_CHECK_FALSE( ... ) (void)(0)
|
|
||||||
|
|
||||||
// "BDD-style" convenience wrappers
|
|
||||||
#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ) )
|
|
||||||
#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className )
|
|
||||||
|
|
||||||
#define GIVEN( desc )
|
|
||||||
#define AND_GIVEN( desc )
|
|
||||||
#define WHEN( desc )
|
|
||||||
#define AND_WHEN( desc )
|
|
||||||
#define THEN( desc )
|
|
||||||
#define AND_THEN( desc )
|
|
||||||
|
|
||||||
#endif // ^^ unprefixed, disabled
|
|
||||||
|
|
||||||
// end of user facing macros
|
|
||||||
|
|
||||||
#endif // CATCH_TEST_MACROS_HPP_INCLUDED
|
|
@ -1,141 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#include <catch2/catch_test_spec.hpp>
|
|
||||||
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
|
|
||||||
#include <catch2/internal/catch_test_case_registry_impl.hpp>
|
|
||||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
|
||||||
#include <catch2/internal/catch_string_manip.hpp>
|
|
||||||
#include <catch2/catch_test_case_info.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
TestSpec::Pattern::Pattern( std::string const& name )
|
|
||||||
: m_name( name )
|
|
||||||
{}
|
|
||||||
|
|
||||||
TestSpec::Pattern::~Pattern() = default;
|
|
||||||
|
|
||||||
std::string const& TestSpec::Pattern::name() const {
|
|
||||||
return m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString )
|
|
||||||
: Pattern( filterString )
|
|
||||||
, m_wildcardPattern( toLower( name ), CaseSensitive::No )
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
|
|
||||||
return m_wildcardPattern.matches( testCase.name );
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestSpec::NamePattern::serializeTo( std::ostream& out ) const {
|
|
||||||
out << '"' << name() << '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
|
|
||||||
: Pattern( filterString )
|
|
||||||
, m_tag( tag )
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
|
|
||||||
return std::find( begin( testCase.tags ),
|
|
||||||
end( testCase.tags ),
|
|
||||||
Tag( m_tag ) ) != end( testCase.tags );
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestSpec::TagPattern::serializeTo( std::ostream& out ) const {
|
|
||||||
out << name();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
|
|
||||||
bool should_use = !testCase.isHidden();
|
|
||||||
for (auto const& pattern : m_required) {
|
|
||||||
should_use = true;
|
|
||||||
if (!pattern->matches(testCase)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto const& pattern : m_forbidden) {
|
|
||||||
if (pattern->matches(testCase)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return should_use;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestSpec::Filter::serializeTo( std::ostream& out ) const {
|
|
||||||
bool first = true;
|
|
||||||
for ( auto const& pattern : m_required ) {
|
|
||||||
if ( !first ) {
|
|
||||||
out << ' ';
|
|
||||||
}
|
|
||||||
out << *pattern;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
for ( auto const& pattern : m_forbidden ) {
|
|
||||||
if ( !first ) {
|
|
||||||
out << ' ';
|
|
||||||
}
|
|
||||||
out << *pattern;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::string TestSpec::extractFilterName( Filter const& filter ) {
|
|
||||||
Catch::ReusableStringStream sstr;
|
|
||||||
sstr << filter;
|
|
||||||
return sstr.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TestSpec::hasFilters() const {
|
|
||||||
return !m_filters.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TestSpec::matches( TestCaseInfo const& testCase ) const {
|
|
||||||
return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
|
|
||||||
}
|
|
||||||
|
|
||||||
TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const {
|
|
||||||
Matches matches;
|
|
||||||
matches.reserve( m_filters.size() );
|
|
||||||
for ( auto const& filter : m_filters ) {
|
|
||||||
std::vector<TestCaseHandle const*> currentMatches;
|
|
||||||
for ( auto const& test : testCases )
|
|
||||||
if ( isThrowSafe( test, config ) &&
|
|
||||||
filter.matches( test.getTestCaseInfo() ) )
|
|
||||||
currentMatches.emplace_back( &test );
|
|
||||||
matches.push_back(
|
|
||||||
FilterMatch{ extractFilterName( filter ), currentMatches } );
|
|
||||||
}
|
|
||||||
return matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TestSpec::vectorStrings& TestSpec::getInvalidSpecs() const {
|
|
||||||
return m_invalidSpecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestSpec::serializeTo( std::ostream& out ) const {
|
|
||||||
bool first = true;
|
|
||||||
for ( auto const& filter : m_filters ) {
|
|
||||||
if ( !first ) {
|
|
||||||
out << ',';
|
|
||||||
}
|
|
||||||
out << filter;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,119 +0,0 @@
|
|||||||
|
|
||||||
// Copyright Catch2 Authors
|
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
|
||||||
// (See accompanying file LICENSE.txt or copy at
|
|
||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
|
||||||
#ifndef CATCH_TEST_SPEC_HPP_INCLUDED
|
|
||||||
#define CATCH_TEST_SPEC_HPP_INCLUDED
|
|
||||||
|
|
||||||
#ifdef __clang__
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpadded"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
|
||||||
#include <catch2/internal/catch_wildcard_pattern.hpp>
|
|
||||||
|
|
||||||
#include <iosfwd>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
|
|
||||||
class IConfig;
|
|
||||||
struct TestCaseInfo;
|
|
||||||
class TestCaseHandle;
|
|
||||||
|
|
||||||
class TestSpec {
|
|
||||||
|
|
||||||
class Pattern {
|
|
||||||
public:
|
|
||||||
explicit Pattern( std::string const& name );
|
|
||||||
virtual ~Pattern();
|
|
||||||
virtual bool matches( TestCaseInfo const& testCase ) const = 0;
|
|
||||||
std::string const& name() const;
|
|
||||||
private:
|
|
||||||
virtual void serializeTo( std::ostream& out ) const = 0;
|
|
||||||
// Writes string that would be reparsed into the pattern
|
|
||||||
friend std::ostream& operator<<(std::ostream& out,
|
|
||||||
Pattern const& pattern) {
|
|
||||||
pattern.serializeTo( out );
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string const m_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NamePattern : public Pattern {
|
|
||||||
public:
|
|
||||||
explicit NamePattern( std::string const& name, std::string const& filterString );
|
|
||||||
bool matches( TestCaseInfo const& testCase ) const override;
|
|
||||||
private:
|
|
||||||
void serializeTo( std::ostream& out ) const override;
|
|
||||||
|
|
||||||
WildcardPattern m_wildcardPattern;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TagPattern : public Pattern {
|
|
||||||
public:
|
|
||||||
explicit TagPattern( std::string const& tag, std::string const& filterString );
|
|
||||||
bool matches( TestCaseInfo const& testCase ) const override;
|
|
||||||
private:
|
|
||||||
void serializeTo( std::ostream& out ) const override;
|
|
||||||
|
|
||||||
std::string m_tag;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Filter {
|
|
||||||
std::vector<Detail::unique_ptr<Pattern>> m_required;
|
|
||||||
std::vector<Detail::unique_ptr<Pattern>> m_forbidden;
|
|
||||||
|
|
||||||
//! Serializes this filter into a string that would be parsed into
|
|
||||||
//! an equivalent filter
|
|
||||||
void serializeTo( std::ostream& out ) const;
|
|
||||||
friend std::ostream& operator<<(std::ostream& out, Filter const& f) {
|
|
||||||
f.serializeTo( out );
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool matches( TestCaseInfo const& testCase ) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::string extractFilterName( Filter const& filter );
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct FilterMatch {
|
|
||||||
std::string name;
|
|
||||||
std::vector<TestCaseHandle const*> tests;
|
|
||||||
};
|
|
||||||
using Matches = std::vector<FilterMatch>;
|
|
||||||
using vectorStrings = std::vector<std::string>;
|
|
||||||
|
|
||||||
bool hasFilters() const;
|
|
||||||
bool matches( TestCaseInfo const& testCase ) const;
|
|
||||||
Matches matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const;
|
|
||||||
const vectorStrings & getInvalidSpecs() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Filter> m_filters;
|
|
||||||
std::vector<std::string> m_invalidSpecs;
|
|
||||||
|
|
||||||
friend class TestSpecParser;
|
|
||||||
//! Serializes this test spec into a string that would be parsed into
|
|
||||||
//! equivalent test spec
|
|
||||||
void serializeTo( std::ostream& out ) const;
|
|
||||||
friend std::ostream& operator<<(std::ostream& out,
|
|
||||||
TestSpec const& spec) {
|
|
||||||
spec.serializeTo( out );
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __clang__
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // CATCH_TEST_SPEC_HPP_INCLUDED
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user