diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml new file mode 100644 index 00000000..07382034 --- /dev/null +++ b/.github/workflows/compile-examples.yml @@ -0,0 +1,154 @@ +name: Compile Examples + +on: + pull_request: + paths: + - ".github/workflows/compile-examples.yml" + - "cores/**" + - "libraries/**" + - "variants/**" + - "boards.txt" + - "platform.txt" + push: + paths: + - ".github/workflows/compile-examples.yml" + - "cores/**" + - "libraries/**" + - "variants/**" + - "boards.txt" + - "platform.txt" + +jobs: + compile-test: + runs-on: ubuntu-latest + + env: + # sketch paths to compile (recursive) for all boards + UNIVERSAL_SKETCH_PATHS: | + - extras/examples + - libraries/Wire + - libraries/SPI + - libraries/SoftwareSerial + - libraries/EEPROM + - ~/Arduino/libraries/Servo/examples + - ~/Arduino/libraries/LiquidCrystal/examples + - ~/Arduino/libraries/MFRC522/examples + - ~/Arduino/libraries/Ethernet/examples + - ~/Arduino/libraries/Adafruit_MQTT_Library/examples/mqtt_ethernet + - ~/Arduino/libraries/ArduinoBearSSL/examples/SHA1 + - ~/Arduino/libraries/ArduinoBearSSL/examples/SHA256 + - ~/Arduino/libraries/Arduino_LSM9DS1/examples + - ~/Arduino/libraries/SD/examples + - ~/Arduino/libraries/Arduino_JSON/examples + - ~/Arduino/libraries/TFT/examples/Arduino/TFTBitmapLogo + - ~/Arduino/libraries/TFT/examples/Arduino/TFTColorPicker + - ~/Arduino/libraries/TFT/examples/Arduino/TFTDisplayText + - ~/Arduino/libraries/TFT/examples/Arduino/TFTEtchASketch + - ~/Arduino/libraries/TFT/examples/Arduino/TFTGraph + - ~/Arduino/libraries/TFT/examples/Arduino/TFTPong + - ~/Arduino/libraries/Arduino_CRC32/examples + - ~/Arduino/libraries/Arduino_LSM6DS3/examples + - ~/Arduino/libraries/Stepper/examples + - ~/Arduino/libraries/Arduino_HTS221/examples + - ~/Arduino/libraries/Arduino_DebugUtils/examples + - ~/Arduino/libraries/Arduino_LPS22HB/examples + - ~/Arduino/libraries/ArduinoDMX/examples + - ~/Arduino/libraries/ArduinoRS485/examples + SKETCHES_REPORTS_PATH: sketches-reports + + strategy: + fail-fast: false + + matrix: + board: [ + {"fqbn": "arduino:megaavr:uno2018:mode=on", "type": "UnoWiFiRev2"}, + {"fqbn": "arduino:megaavr:uno2018:mode=off", "type": "UnoWiFiRev2"}, + {"fqbn": "arduino:megaavr:nona4809", "type": "NanoEvery"} + ] + + # make board type-specific customizations to the matrix jobs + include: + # Uno WiFi Rev2 + - board: + type: "UnoWiFiRev2" + additional-sketch-paths: | + - ~/Arduino/libraries/WiFiNINA/examples + - ~/Arduino/libraries/ArduinoMqttClient/examples + - ~/Arduino/libraries/Arduino_OAuth/examples/Tweeter + # Nano Every + - board: + type: "NanoEvery" + additional-sketch-paths: + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # The source files are in a subfolder of the ArduinoCore-API repository, so it's not possible to clone it directly to the final destination in the core + - name: Checkout ArduinoCore-API + uses: actions/checkout@v2 + with: + repository: arduino/ArduinoCore-API + path: extras/ArduinoCore-API + + - name: Install ArduinoCore-API + run: mv "$GITHUB_WORKSPACE/extras/ArduinoCore-API/api" "$GITHUB_WORKSPACE/cores/arduino" + + - name: Checkout Basic examples + uses: actions/checkout@v2 + with: + repository: arduino/arduino-examples + path: extras + + - name: Delete incompatible examples + run: | + # These boards do not support native USB + rm -r "$GITHUB_WORKSPACE/extras/examples/09.USB" + # The next command can be removed after the core integration with ArduinoCore-API + rm -r "$GITHUB_WORKSPACE/extras/examples/10.StarterKit_BasicKit/p11_CrystalBall" + # CapacitiveSensor library does not support megaAVR core yet + rm -r "$GITHUB_WORKSPACE/extras/examples/10.StarterKit_BasicKit/p13_TouchSensorLamp" + - name: Compile examples + uses: arduino/compile-sketches@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fqbn: ${{ matrix.board.fqbn }} + libraries: | + - name: Adafruit MQTT Library + - name: Servo + - name: LiquidCrystal + - name: MFRC522 + - name: Ethernet + - name: ArduinoBearSSL + - name: Arduino_LSM9DS1 + - name: TFT + - name: ArduinoMqttClient + - name: Arduino_CRC32 + - name: Arduino_LSM6DS3 + - name: Stepper + - name: SD + - name: Arduino_JSON + - name: Arduino_HTS221 + - name: Arduino_DebugUtils + - name: Arduino_LPS22HB + - name: ArduinoDMX + - name: ArduinoRS485 + - name: Arduino_OAuth + - name: WiFiNINA + platforms: | + # Use Board Manager to install the latest release of Arduino megaAVR Boards to get the toolchain + - name: "arduino:megaavr" + # Overwrite the Board Manager installation with the local platform + - source-path: "./" + name: "arduino:megaavr" + sketch-paths: | + ${{ env.UNIVERSAL_SKETCH_PATHS }} + ${{ matrix.additional-sketch-paths }} + enable-deltas-report: 'true' + sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} + + - name: Save memory usage change report as artifact + uses: actions/upload-artifact@v2 + with: + path: ${{ env.SKETCHES_REPORTS_PATH }} + name: ${{ env.SKETCHES_REPORTS_PATH }} diff --git a/.github/workflows/compile-muxto.yml b/.github/workflows/compile-muxto.yml new file mode 100644 index 00000000..881699af --- /dev/null +++ b/.github/workflows/compile-muxto.yml @@ -0,0 +1,105 @@ +name: Compile MuxTO + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/compile-muxto.yml" + - "firmwares/MuxTO/**" + pull_request: + paths: + - ".github/workflows/compile-muxto.yml" + - "firmwares/MuxTO/**" + schedule: + # Run every Tuesday at 8 AM UTC to catch breakage caused by changes to external resources (libraries, platforms). + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +env: + BINARY_FILENAME: MuxTO.ino.bin + BINARY_ARTIFACT_NAME: MuxTO + +jobs: + build: + name: Build firmware + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + matrix: + board: + - fqbn: arduino:samd:muxto:float=default,config=enabled,clock=internal_usb,timer=timer_732Hz,bootloader=4kb,serial=two_uart,usb=cdc + platforms: | + # Install MattairTech_Arduino:samd via Boards Manager for the toolchain + - name: MattairTech_Arduino:samd + source-url: https://www.mattairtech.com/software/arduino/package_MattairTech_index.json + # This needs to match with the version of MattairTech_Arduino:samd the Arduino fork is based on in order to get the right tool versions + version: 1.6.17 + # Install officila samd version for compiler support + - name: arduino:samd + # Install the platform with MuxTO support + - name: arduino:samd + source-url: https://github.com/arduino/ArduinoCore-samd.git + version: muxto + + steps: + - name: Set environment variables + run: | + # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable + echo "BINARY_OUTPUT_PATH=${{ runner.temp }}/output" >> "$GITHUB_ENV" + echo "SKETCHES_REPORTS_PATH=${{ runner.temp }}/sketches-reports" >> "$GITHUB_ENV" + + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Compile firmware + uses: arduino/compile-sketches@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fqbn: ${{ matrix.board.fqbn }} + platforms: ${{ matrix.board.platforms }} + libraries: | + - + sketch-paths: | + - firmwares/MuxTO + cli-compile-flags: | + - --output-dir=${{ env.BINARY_OUTPUT_PATH }} + enable-deltas-report: true + sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} + + - name: Save firmware binary as workflow artifact + uses: actions/upload-artifact@v2 + with: + if-no-files-found: error + path: ${{ env.BINARY_OUTPUT_PATH }}/${{ env.BINARY_FILENAME }} + name: ${{ env.BINARY_ARTIFACT_NAME }} + + - name: Save sketches report as workflow artifact + uses: actions/upload-artifact@v2 + with: + if-no-files-found: error + path: ${{ env.SKETCHES_REPORTS_PATH }} + name: sketches-reports + + size: + name: Check firmware size + needs: build + runs-on: ubuntu-latest + steps: + - name: Download binary artifact + uses: actions/download-artifact@v2 + with: + name: ${{ env.BINARY_ARTIFACT_NAME }} + + # The normal size check done by Arduino CLI is not working correctly, so it's necessary to check the size directly + - name: Check firmware binary size + run: | + BINARY_SIZE="$(stat --printf="%s" "${{ github.workspace }}/${{ env.BINARY_FILENAME }}")" + MAX_BINARY_SIZE=$((12 * 1024)) + echo "File size: ${BINARY_SIZE}/${MAX_BINARY_SIZE} B" + if [[ $BINARY_SIZE -gt $MAX_BINARY_SIZE ]]; then + echo "::error::Binary size of $BINARY_SIZE B exceeds the available memory ($MAX_BINARY_SIZE B)" + exit 1 + fi diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..d7e23178 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,107 @@ +name: release + +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+*" + +jobs: + core-pre-release-from-tag: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Checkout ArduinoCore-API + uses: actions/checkout@v2 + with: + repository: arduino/ArduinoCore-API + path: extras/ArduinoCore-API + + - name: Check if API should be compiled in the core + id: checkapi + run: | + if [[ $(grep -r api platform.txt) ]]; then echo "::set-output name=IS_API::true"; fi + + - name: Checkout latest tag of ArduinoCore-API and add it to the core + run: | + cd extras/ArduinoCore-API + git fetch --tags + git checkout $(git describe --tags $(git rev-list --tags --max-count=1)) + cd ../.. + mv "$GITHUB_WORKSPACE/extras/ArduinoCore-API/api" "$GITHUB_WORKSPACE/cores/arduino" + if: steps.checkapi.outputs.IS_API == 'true' + + - name: Remove ArduinoCore-API + run: rm -r "$GITHUB_WORKSPACE/extras/ArduinoCore-API" + + - name: Set env + run: echo "TAG_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Get repo name + run: echo "REPOSITORY_NAME=$(echo ${{ github.repository }} | cut -d "/" -f2-)" >> $GITHUB_ENV + + - name: Package the new core + run: | + extras/pack.release.bash $TAG_VERSION $REPOSITORY_NAME + cd extras + mkdir staging + echo $PWD + mv ../*.json staging/ + mv ../*.tar.bz2 staging/ + cd .. + + - name: Get architecture name + run: | + echo "ARCHITECTURE=$(cat extras/package_index.json.NewTag.template | jq ".packages[0].platforms[0].architecture" | sed 's/\"//g')" >> $GITHUB_ENV + + - name: Upload package_*_index.json and *.tar.bz2 file to Arduino downloads servers + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: | + aws s3 sync extras/staging/ s3://${{ secrets.ARDUINO_DOWNLOADS_BUCKET }}/packages/staging/ --exclude "*" --include *.json + aws s3 sync extras/staging/ s3://${{ secrets.ARDUINO_DOWNLOADS_BUCKET }}/cores/staging/ --exclude "*" --include *.tar.bz2 + + - name: Checkout Basic examples + uses: actions/checkout@v2 + with: + repository: arduino/arduino-examples + path: extras/arduino-examples + + - name: Install Arduino CLI + uses: arduino/setup-arduino-cli@v1.1.1 + with: + version: "0.14.0" + + - name: Download and verify new core + run: | + export PATH=$PATH:$PWD + arduino-cli version + cp extras/staging/package_${REPOSITORY_NAME}_${TAG_VERSION}_index.json . + export ARDUINO_DIRECTORIES_DATA=$PWD + export ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS=file://$PWD/package_${REPOSITORY_NAME}_${TAG_VERSION}_index.json + arduino-cli config init + arduino-cli config dump -v + arduino-cli core update-index -v + arduino-cli core install arduino:${ARCHITECTURE}@${TAG_VERSION} + INDEX=0 + arduino-cli board listall --format=json > boardlist.json + N=$(jq '.boards | length' boardlist.json) + let N=N-1 + echo $N + for INDEX in $(seq 0 1 $N); do arduino-cli compile --fqbn $(cat boardlist.json | jq ".boards[$INDEX].FQBN" | sed 's/\"//g') $PWD/extras/arduino-examples/examples/01.Basics/Blink; done + + # See: https://github.com/rtCamp/action-slack-notify + - name: Slack notification of core pre-release + uses: rtCamp/action-slack-notify@v2.1.0 + env: + SLACK_CHANNEL: core_releases + SLACK_COLOR: good + SLACK_USERNAME: ArduinoBot + SLACK_ICON: https://github.com/arduino.png?size=48 + SLACK_TITLE: Arduino core pre-release + SLACK_MESSAGE: 'Version ${{ env.TAG_VERSION }} of core ${{ env.REPOSITORY_NAME }} is now available' + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + MSG_MINIMAL: true \ No newline at end of file diff --git a/.github/workflows/report-size-deltas.yml b/.github/workflows/report-size-deltas.yml new file mode 100644 index 00000000..1e37a511 --- /dev/null +++ b/.github/workflows/report-size-deltas.yml @@ -0,0 +1,16 @@ +name: Report PR Size Deltas + +on: + schedule: + - cron: '*/5 * * * *' + +jobs: + report: + runs-on: ubuntu-latest + + steps: + - name: Comment size deltas reports to PRs + uses: arduino/report-size-deltas@v1 + with: + # The name of the workflow artifact created by the sketch compilation workflow + sketches-reports-source: sketches-reports diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..bf9d2701 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cores/test/external/libvireg"] + path = cores/test/external/libvireg + url = https://github.com/bcmi-labs/libvireg diff --git a/README.md b/README.md index f5ace4fd..4090cd7e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Arduino Core for ATMEGA4809 CPU +[![Compile MuxTO status](https://github.com/arduino/ArduinoCore-megaavr/actions/workflows/compile-muxto.yml/badge.svg)](https://github.com/arduino/ArduinoCore-megaavr/actions/workflows/compile-muxto.yml) + This repository contains the source code and configuration files of the Arduino Core for Microchip's ATMEGA4809 processor (used on the Arduino Uno WiFi Rev2 boards). diff --git a/boards.txt b/boards.txt index 7bdd1767..504c40af 100644 --- a/boards.txt +++ b/boards.txt @@ -1,29 +1,40 @@ -# See: http://code.google.com/p/arduino/wiki/Platforms +# See: https://arduino.github.io/arduino-cli/latest/platform-specification/ ############################################################## -uno2018.name=Arduino Uno WiFi Rev2 +uno2018.name=Arduino UNO WiFi Rev2 uno2018.vid.0=0x03eb uno2018.pid.0=0x2145 +uno2018.upload_port.0.vid=0x03eb +uno2018.upload_port.0.pid=0x2145 +uno2018.upload_port.1.board=uno2018 uno2018.upload.tool=avrdude +uno2018.upload.tool.default=avrdude +uno2018.upload.tool.network=arduino_ota uno2018.upload.protocol=xplainedmini_updi -uno2018.upload.maximum_size=49152 +uno2018.upload.maximum_size=48640 uno2018.upload.maximum_data_size=6144 uno2018.upload.speed=115200 +uno2018.upload.extra_params=-Pusb +uno2018.upload.extra_files="-Uflash:w:{runtime.platform.path}/bootloaders/{bootloader.file}:i" uno2018.build.mcu=atmega4809 uno2018.build.f_cpu=16000000L uno2018.build.board=AVR_UNO_WIFI_REV2 uno2018.build.core=arduino uno2018.build.variant=uno2018 +uno2018.build.text_section_start=.text=0x200 uno2018.build.extra_flags={build.328emulation} -DMILLIS_USE_TIMERB3 #uno2018.build.extra_flags=-B{runtime.tools.atpack.path}/gcc/dev/{build.mcu} uno2018.bootloader.tool=avrdude +uno2018.bootloader.tool.default=avrdude +uno2018.bootloader.file=atmega4809_uart_bl.hex uno2018.bootloader.SYSCFG0=0xC9 -uno2018.bootloader.BOOTEND=0x00 +uno2018.bootloader.BOOTEND=0x02 +uno2018.bootloader.OSCCFG=0x01 uno2018.fuses.file=fuses_4809.bin menu.mode=Registers emulation @@ -31,4 +42,48 @@ uno2018.menu.mode.on=ATMEGA328 uno2018.menu.mode.on.build.328emulation=-DUNO_WIFI_REV2_328MODE uno2018.menu.mode.off=None (ATMEGA4809) uno2018.menu.mode.off.build.328emulation= + +############################################################## + +nona4809.name=Arduino Nano Every + +nona4809.vid.0=0x2341 +nona4809.pid.0=0x0058 +nona4809.upload_port.0.vid=0x2341 +nona4809.upload_port.0.pid=0x0058 +nona4809.upload_port.1.board=nona4809 + +nona4809.upload.tool=avrdude +nona4809.upload.tool.default=avrdude +nona4809.upload.tool.network=arduino_ota +nona4809.upload.protocol=jtag2updi +nona4809.upload.maximum_size=49152 +nona4809.upload.maximum_data_size=6144 +nona4809.upload.speed=115200 +nona4809.upload.use_1200bps_touch=true +nona4809.upload.extra_params=-P{serial.port} + +nona4809.build.mcu=atmega4809 +nona4809.build.f_cpu=16000000L +nona4809.build.board=AVR_NANO_EVERY +nona4809.build.core=arduino +nona4809.build.variant=nona4809 +nona4809.build.text_section_start=.text=0x0 +nona4809.build.extra_flags={build.328emulation} -DMILLIS_USE_TIMERB3 -DNO_EXTERNAL_I2C_PULLUP +#nona4809.build.extra_flags=-B{runtime.tools.atpack.path}/gcc/dev/{build.mcu} + +nona4809.bootloader.tool=avrdude +nona4809.bootloader.tool.default=avrdude +nona4809.bootloader.file=atmega4809_uart_bl.hex +nona4809.bootloader.SYSCFG0=0xC9 +nona4809.bootloader.BOOTEND=0x00 +nona4809.bootloader.OSCCFG=0x01 +nona4809.fuses.file=fuses_4809.bin + +menu.mode=Registers emulation +nona4809.menu.mode.on=ATMEGA328 +nona4809.menu.mode.on.build.328emulation=-DAVR_NANO_4809_328MODE +nona4809.menu.mode.off=None (ATMEGA4809) +nona4809.menu.mode.off.build.328emulation= + ############################################################## diff --git a/bootloaders/atmega4809_uart_bl.hex b/bootloaders/atmega4809_uart_bl.hex new file mode 100644 index 00000000..8be19cfa --- /dev/null +++ b/bootloaders/atmega4809_uart_bl.hex @@ -0,0 +1,20 @@ +:100000001124669A26E030E408C000000197E9F761 +:100010008DB183278DB9215019F081E091E7F6CF9A +:1000200080911F138B3E69F48FEF80931F138DE92E +:1000300093E084BF909300108091021081FDFCCF6B +:1000400006C082E080930110E0E0F1E0099580ECC9 +:1000500080930608A09123110A2E000CBB0BBC5FF5 +:100060002CE530E00E9476007E5F8F4F9F4F2AE0A4 +:1000700095958795779567952A95D1F760930808A8 +:10008000709309088091E20581608093E205049AEB +:10009000E0E0F2E42DE933E040E41EC08091040882 +:1000A00087FFFCCF80910008E11552E4F50711F4B9 +:1000B000882391F0809302088193CF018F7799274D +:1000C000892B51F424BF309300108091021080FDE1 +:1000D000FCCF8DB184278DB9CF0190548115904C00 +:1000E000E8F288ED91E084BF909341000E947D008A +:1000F000B7FF0895821B930B0895A29FB001B39F91 +:10010000C001A39F700D811D1124911DB29F700D20 +:08011000811D1124911D0895C9 +:02000004008278 +:00000001FF diff --git a/bootloaders/boot.c b/bootloaders/boot.c new file mode 100644 index 00000000..1a8dcb1e --- /dev/null +++ b/bootloaders/boot.c @@ -0,0 +1,215 @@ +/****************************************************************************** + * © 2018 Microchip Technology Inc. and its subsidiaries. + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY + * IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR + * EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, + * EVEN IF MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + * + *****************************************************************************/ + +/* + * UART Bootloader for tinyAVR 0- and 1-series, and megaAVR 0-series + * Each byte received is echoed to confirm reception. + * + * For the code to be placed in the constructors section it is necessary + * to disable standard startup files in Toolchain->AVR/GNU Linker->General. + * + * This file is targeted to UNO WiFi REV2 (ATmega4809) + * USART0 TxD PA4 + * USART0 RxD PA5 + * LED0 PD6 + * SW1 PC1 (external pull-up) + */ +#define F_CPU_RESET (16E6/6) + +#include +#include +#include + +/* Baud rate configuration */ +#define BOOT_BAUD (115200) + +#define BAUD_REG_VAL (F_CPU_RESET * 64) / (BOOT_BAUD * 16) + +/* Memory configuration + * BOOTEND_FUSE * 256 must be above Bootloader Program Memory Usage, + * this is 194 bytes at optimization level -O3, so BOOTEND_FUSE = 0x01 + */ +#define BOOTEND_FUSE (0x02) +#define BOOT_SIZE (BOOTEND_FUSE * 0x100) +#define MAPPED_APPLICATION_START (MAPPED_PROGMEM_START + BOOT_SIZE) +#define MAPPED_APPLICATION_SIZE (MAPPED_PROGMEM_SIZE - BOOT_SIZE) + +/* Fuse configuration + * BOOTEND sets the size (end) of the boot section in blocks of 256 bytes. + * APPEND = 0x00 defines the section from BOOTEND*256 to end of Flash as application code. + * Remaining fuses have default configuration. + */ +FUSES = { + .OSCCFG = FREQSEL_16MHZ_gc, + .SYSCFG0 = CRCSRC_NOCRC_gc | RSTPINCFG_UPDI_gc, + .SYSCFG1 = SUT_64MS_gc, + .APPEND = 0x00, + .BOOTEND = BOOTEND_FUSE +}; + +/* Define application pointer type */ +typedef void (*const app_t)(void); + +/* Interface function prototypes */ +static inline bool is_bootloader_requested(void); +static inline void init_uart(void); +static inline uint8_t uart_receive(void); +static inline void uart_send(uint8_t byte); +static inline void init_status_led(void); +static inline void toggle_status_led(void); +static inline void wait_50_ms(void); + +/* + * Main boot function + * Put in the constructors section (.ctors) to save Flash. + * Naked attribute used since function prologue and epilogue is unused + */ +__attribute__((naked)) __attribute__((section(".ctors"))) void boot(void) +{ + /* Initialize system for AVR GCC support, expects r1 = 0 */ + asm volatile("clr r1"); + + /* 3 very fast blinks (to match optiboot behaviour) */ + init_status_led(); + for (uint8_t i = 0; i < 6; i++) { + wait_50_ms(); + toggle_status_led(); + } + + /* Check if entering application or continuing to bootloader */ + if(!is_bootloader_requested()) { + /* Enable Boot Section Lock */ + NVMCTRL.CTRLB = NVMCTRL_BOOTLOCK_bm; + + /* Go to application, located immediately after boot section */ + app_t app = (app_t)(BOOT_SIZE / sizeof(app_t)); + app(); + } + + /* Initialize communication interface */ + init_uart(); + + /* + * Start programming at start for application section + * Subtract MAPPED_PROGMEM_START in condition to handle overflow on large flash sizes + */ + uint8_t *app_ptr = (uint8_t *)MAPPED_APPLICATION_START; + while(app_ptr - MAPPED_PROGMEM_START <= (uint8_t *)PROGMEM_END) { + /* Receive and echo data before loading to memory */ + uint8_t rx_data = uart_receive(); + if (app_ptr == (uint8_t *)MAPPED_APPLICATION_START && rx_data == 0) { + // skip first character if 0x00 + continue; + } + uart_send(rx_data); + + /* Incremental load to page buffer before writing to Flash */ + *app_ptr = rx_data; + app_ptr++; + if(!((uint16_t)app_ptr % MAPPED_PROGMEM_PAGE_SIZE)) { + /* Page boundary reached, Commit page to Flash */ + _PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc); + while(NVMCTRL.STATUS & NVMCTRL_FBUSY_bm); + + toggle_status_led(); + } + } + + /* Issue system reset */ + _PROTECTED_WRITE(RSTCTRL.SWRR, RSTCTRL_SWRE_bm); +} + +/* + * Boot access request function + */ +static inline bool is_bootloader_requested(void) +{ + /* Check for boot request from firmware */ + if (USERROW.USERROW31 == 0xEB) { + /* Clear boot request*/ + USERROW.USERROW31 = 0xff; + _PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc); + while(NVMCTRL.STATUS & NVMCTRL_EEBUSY_bm); + + return true; + } + return false; +} + +/* + * Communication interface functions + */ +static inline void init_uart(void) +{ + /* Configure UART */ + USART0.CTRLB = USART_RXEN_bm | USART_TXEN_bm; + + /* From datasheet: + * Baud rate compensated with factory stored frequency error + * Asynchronous communication without Auto-baud (Sync Field) + * 20MHz Clock, 3V + */ + int32_t baud_reg_val = BAUD_REG_VAL; // ideal BAUD register value + int8_t sigrow_val = SIGROW.OSC16ERR5V; // read signed error + baud_reg_val *= (1024 + sigrow_val); // sum resolution + error + baud_reg_val += 512; // compensate for rounding error + baud_reg_val /= 1024; // divide by resolution + USART0.BAUD = (int16_t) baud_reg_val; // set adjusted baud rate + + PORTMUX.USARTROUTEA |= PORTMUX_USART0_ALT1_gc; + + /* Set TxD (PA4) as output */ + VPORTA.DIR |= PIN4_bm; +} + +static inline uint8_t uart_receive(void) +{ + /* Poll for data received */ + while(!(USART0.STATUS & USART_RXCIF_bm)); + return USART0.RXDATAL; +} + +static inline void uart_send(uint8_t byte) +{ + /* Data will be sent when TXDATA is written */ + USART0.TXDATAL = byte; +} + +static inline void init_status_led(void) +{ + /* Set LED0 (PD6) as output */ + VPORTD.DIR |= PIN6_bm; +} + +static inline void toggle_status_led(void) +{ + /* Toggle LED0 (PD6) */ + VPORTD.OUT ^= PIN6_bm; +} + +static inline void wait_50_ms(void) +{ + for (uint16_t i = 160000; i--; i != 0) { + asm volatile ("nop"); + } +} \ No newline at end of file diff --git a/bootloaders/build.sh b/bootloaders/build.sh new file mode 100755 index 00000000..dd12e873 --- /dev/null +++ b/bootloaders/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +if [ x$AVR_GCC_PATH == x"" ]; then +AVR_GCC_PATH=/bin/ +fi + +echo Compiling +${AVR_GCC_PATH}/avr-gcc -c -g -Os -w -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -Wl,--gc-sections -w -mmcu=atmega4809 -DF_CPU=16000000L boot.c -o boot.o +${AVR_GCC_PATH}/avr-gcc -g -Os -w -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -nostartfiles -Wl,--gc-sections -w -mmcu=atmega4809 -DF_CPU=16000000L boot.o -o boot.elf + +echo Extracting bin +${AVR_GCC_PATH}/avr-objcopy -O ihex -R .fuses boot.elf boot.hex +#${AVR_GCC_PATH}avr-objcopy -O binary -j .fuses --set-section-flags=.fuses=alloc,load --no-change-warnings --change-section-lma .fuses=0 boot.elf boot.fuses + +echo Moving hex +mv boot.hex atmega4809_uart_bl.hex + +#${AVR_GCC_PATH}/../avrdude/6.3.0-arduino14/bin/avrdude -C${AVR_GCC_PATH}/../avrdude/6.3.0-arduino14/etc/avrdude.conf -v -patmega4809 -cxplainedmini_updi -Pusb -Ufuses:w:boot.fuses:r -Uflash:w:boot.bin:r diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index 1892a7a1..c40dcb20 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -83,10 +83,11 @@ unsigned long microsecondsToClockCycles(unsigned long microseconds); // Get the bit location within the hardware port of the given virtual pin. // This comes from the pins_*.c file for the active board configuration. -extern const uint8_t PROGMEM digital_pin_to_port[]; -extern const uint8_t PROGMEM digital_pin_to_bit_mask[]; -extern const uint8_t PROGMEM digital_pin_to_bit_position[]; -extern const uint8_t PROGMEM digital_pin_to_timer[]; +extern const uint8_t digital_pin_to_port[]; +extern const uint8_t digital_pin_to_bit_mask[]; +extern const uint8_t digital_pin_to_bit_position[]; +extern const uint8_t digital_pin_to_timer[]; +extern const uint8_t analog_pin_to_channel[]; // Get the bit location within the hardware port of the given virtual pin. // This comes from the pins_*.c file for the active board configuration. @@ -115,12 +116,12 @@ extern const uint8_t PROGMEM digital_pin_to_timer[]; void setup_timers(); bool isDoubleBondedActive(uint8_t pin); -#define digitalPinToPort(pin) ( (pin < NUM_TOTAL_PINS) ? pgm_read_byte(digital_pin_to_port + pin) : NOT_A_PIN ) -#define digitalPinToBitPosition(pin) ( (pin < NUM_TOTAL_PINS) ? pgm_read_byte(digital_pin_to_bit_position + pin) : NOT_A_PIN ) -#define analogPinToBitPosition(pin) ( (pin < NUM_ANALOG_INPUTS) ? pgm_read_byte(digital_pin_to_bit_position + pin + ANALOG_INPUT_OFFSET) : NOT_A_PIN ) -#define digitalPinToBitMask(pin) ( (pin < NUM_TOTAL_PINS) ? pgm_read_byte(digital_pin_to_bit_mask + pin) : NOT_A_PIN ) -#define analogPinToBitMask(pin) ( (pin < NUM_ANALOG_INPUTS) ? pgm_read_byte(digital_pin_to_bit_mask + pin + ANALOG_INPUT_OFFSET) : NOT_A_PIN ) -#define digitalPinToTimer(pin) ( (pin < NUM_TOTAL_PINS) ? pgm_read_byte(digital_pin_to_timer + pin) : NOT_ON_TIMER ) +#define digitalPinToPort(pin) ( (pin < NUM_TOTAL_PINS) ? digital_pin_to_port[pin] : NOT_A_PIN ) +#define digitalPinToBitPosition(pin) ( (pin < NUM_TOTAL_PINS) ? digital_pin_to_bit_position[pin] : NOT_A_PIN ) +#define digitalPinToBitMask(pin) ( (pin < NUM_TOTAL_PINS) ? digital_pin_to_bit_mask[pin] : NOT_A_PIN ) +#define digitalPinToTimer(pin) ( (pin < NUM_TOTAL_PINS) ? digital_pin_to_timer[pin] : NOT_ON_TIMER ) +#define analogPinToBitPosition(pin) ( (digitalPinToAnalogInput(pin) != NOT_A_PIN) ? digital_pin_to_bit_position[pin + ANALOG_INPUT_OFFSET] : NOT_A_PIN ) +#define analogPinToBitMask(pin) ( (digitalPinToAnalogInput(pin) != NOT_A_PIN) ? digital_pin_to_bit_mask[pin + ANALOG_INPUT_OFFSET] : NOT_A_PIN ) #define portToPortStruct(port) ( (port < NUM_TOTAL_PORTS) ? ((PORT_t *)&PORTA + port) : NULL) #define digitalPinToPortStruct(pin) ( (pin < NUM_TOTAL_PINS) ? ((PORT_t *)&PORTA + digitalPinToPort(pin)) : NULL) @@ -143,11 +144,21 @@ bool isDoubleBondedActive(uint8_t pin); #ifdef UNO_WIFI_REV2_328MODE #include #endif +#ifdef AVR_NANO_4809_328MODE +#include +#endif #if defined(HAVE_HWSERIAL0) && defined(HAVE_CDCSERIAL) #error "Targets with both UART0 and CDC serial not supported" #endif #endif +#ifdef __cplusplus +extern "C" { +#endif #include "pins_arduino.h" +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/cores/arduino/NANO_compat.cpp b/cores/arduino/NANO_compat.cpp new file mode 100644 index 00000000..997d36e3 --- /dev/null +++ b/cores/arduino/NANO_compat.cpp @@ -0,0 +1,305 @@ +/* + Copyright (c) 2019 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "NANO_compat.h" + +#ifdef AVR_NANO_4809_328MODE + +#warning "ATMEGA328 registers emulation is enabled. You may encounter some speed issue. Please consider to disable it in the Tools menu" + +#define PORTA_ARDUINO (*(PORT_t *) 0x0400) /* I/O Ports */ +#define PORTB_ARDUINO (*(PORT_t *) 0x0420) /* I/O Ports */ +#define PORTC_ARDUINO (*(PORT_t *) 0x0440) /* I/O Ports */ +#define PORTD_ARDUINO (*(PORT_t *) 0x0460) /* I/O Ports */ +#define PORTE_ARDUINO (*(PORT_t *) 0x0480) /* I/O Ports */ +#define PORTF_ARDUINO (*(PORT_t *) 0x04A0) /* I/O Ports */ + +#ifndef HOST_BUILD + DDRBClass DDRB (&PORTB_ARDUINO, &PORTE_ARDUINO); + PORTBClass PORTB(&PORTB_ARDUINO, &PORTE_ARDUINO); + DDRCClass DDRC (&PORTA_ARDUINO, &PORTD_ARDUINO); + PORTCClass PORTC(&PORTA_ARDUINO, &PORTD_ARDUINO); + DDRDClass DDRD (&PORTA_ARDUINO, &PORTB_ARDUINO, &PORTC_ARDUINO, &PORTF_ARDUINO); + PORTDClass PORTD(&PORTA_ARDUINO, &PORTB_ARDUINO, &PORTC_ARDUINO, &PORTF_ARDUINO); +#endif /* HOST_BUILD */ + +/*****************************************************************************/ + +inline void set_dir_register(uint8_t const value, uint8_t const nano_328_bp, PORT_t * nano_4809_port, uint8_t const nano_4809_bp) { + if(value & (1 << nano_328_bp)) nano_4809_port->DIRSET = (1 << nano_4809_bp); +} + +inline void clr_dir_register(uint8_t const value, uint8_t const nano_328_bp, PORT_t * nano_4809_port, uint8_t const nano_4809_bp) { + if(~value & (1 << nano_328_bp)) nano_4809_port->DIRCLR = (1 << nano_4809_bp); +} + +inline void set_or_clr_dir_register(uint8_t const value, uint8_t const nano_328_bp, PORT_t * nano_4809_port, uint8_t const nano_4809_bp) { + (value & (1 << nano_328_bp)) ? nano_4809_port->DIRSET = (1 << nano_4809_bp) : nano_4809_port->DIRCLR = (1 << nano_4809_bp); +} + +inline void set_out_register(uint8_t const value, uint8_t const nano_328_bp, PORT_t * nano_4809_port, uint8_t const nano_4809_bp) { + if(value & (1 << nano_328_bp)) nano_4809_port->OUTSET = (1 << nano_4809_bp); +} + +inline void clr_out_register(uint8_t const value, uint8_t const nano_328_bp, PORT_t * nano_4809_port, uint8_t const nano_4809_bp) { + if(~value & (1 << nano_328_bp)) nano_4809_port->OUTCLR = (1 << nano_4809_bp); +} + +inline void set_or_clr_out_register(uint8_t const value, uint8_t const nano_328_bp, PORT_t * nano_4809_port, uint8_t const nano_4809_bp) { + (value & (1 << nano_328_bp)) ? nano_4809_port->OUTSET = (1 << nano_4809_bp) : nano_4809_port->OUTCLR = (1 << nano_4809_bp); +} + +/*****************************************************************************/ + +DDRBClass::DDRBClass(PORT_t * portb, PORT_t * porte) +: _portb(portb), + _porte(porte) { } + +DDRBClass & DDRBClass::operator = (uint8_t const value) { + set_or_clr_dir_register(value, 0, _porte, 3); + set_or_clr_dir_register(value, 1, _portb, 0); + set_or_clr_dir_register(value, 2, _portb, 1); + set_or_clr_dir_register(value, 3, _porte, 0); + set_or_clr_dir_register(value, 4, _porte, 1); + set_or_clr_dir_register(value, 5, _porte, 2); + return *this; +} + +DDRBClass & DDRBClass::operator &= (uint8_t const value) { + clr_dir_register(value, 0, _porte, 3); + clr_dir_register(value, 1, _portb, 0); + clr_dir_register(value, 2, _portb, 1); + clr_dir_register(value, 3, _porte, 0); + clr_dir_register(value, 4, _porte, 1); + clr_dir_register(value, 5, _porte, 2); + return *this; +} + +DDRBClass & DDRBClass::operator |= (uint8_t const value) { + set_dir_register(value, 0, _porte, 3); + set_dir_register(value, 1, _portb, 0); + set_dir_register(value, 2, _portb, 1); + set_dir_register(value, 3, _porte, 0); + set_dir_register(value, 4, _porte, 1); + set_dir_register(value, 5, _porte, 2); + return *this; +} + +PORTBClass::PORTBClass(PORT_t * portb, PORT_t * porte) +: _portb(portb), + _porte(porte) { } + +PORTBClass & PORTBClass::operator = (uint8_t const value) { + set_or_clr_out_register(value, 0, _porte, 3); + set_or_clr_out_register(value, 1, _portb, 0); + set_or_clr_out_register(value, 2, _portb, 1); + set_or_clr_out_register(value, 3, _porte, 0); + set_or_clr_out_register(value, 4, _porte, 1); + set_or_clr_out_register(value, 5, _porte, 2); + return *this; +} + +PORTBClass & PORTBClass::operator &= (uint8_t const value) { + clr_out_register(value, 0, _porte, 3); + clr_out_register(value, 1, _portb, 0); + clr_out_register(value, 2, _portb, 1); + clr_out_register(value, 3, _porte, 0); + clr_out_register(value, 4, _porte, 1); + clr_out_register(value, 5, _porte, 2); + return *this; +} + +PORTBClass & PORTBClass::operator |= (uint8_t const value) { + set_out_register(value, 0, _porte, 3); + set_out_register(value, 1, _portb, 0); + set_out_register(value, 2, _portb, 1); + set_out_register(value, 3, _porte, 0); + set_out_register(value, 4, _porte, 1); + set_out_register(value, 5, _porte, 2); + return *this; +} + +/*****************************************************************************/ + +DDRCClass::DDRCClass(PORT_t * porta, PORT_t * portd) +: _porta(porta), + _portd(portd) { } + +DDRCClass & DDRCClass::operator = (uint8_t const value) { + set_or_clr_dir_register(value, 0, _portd, 3); + set_or_clr_dir_register(value, 1, _portd, 2); + set_or_clr_dir_register(value, 2, _portd, 1); + set_or_clr_dir_register(value, 3, _portd, 0); + set_or_clr_dir_register(value, 4, _porta, 2); + set_or_clr_dir_register(value, 5, _porta, 3); + set_or_clr_dir_register(value, 6, _portd, 4); + set_or_clr_dir_register(value, 7, _portd, 5); + return *this; +} + +DDRCClass & DDRCClass::operator &= (uint8_t const value) { + clr_dir_register(value, 0, _portd, 3); + clr_dir_register(value, 1, _portd, 2); + clr_dir_register(value, 2, _portd, 1); + clr_dir_register(value, 3, _portd, 0); + clr_dir_register(value, 4, _porta, 2); + clr_dir_register(value, 5, _porta, 3); + clr_dir_register(value, 6, _portd, 4); + clr_dir_register(value, 7, _portd, 5); + return *this; +} + +DDRCClass & DDRCClass::operator |= (uint8_t const value) { + set_dir_register(value, 0, _portd, 3); + set_dir_register(value, 1, _portd, 2); + set_dir_register(value, 2, _portd, 1); + set_dir_register(value, 3, _portd, 0); + set_dir_register(value, 4, _porta, 2); + set_dir_register(value, 5, _porta, 3); + set_dir_register(value, 6, _portd, 4); + set_dir_register(value, 7, _portd, 5); + return *this; +} + +PORTCClass::PORTCClass(PORT_t * porta, PORT_t * portd) +: _porta(porta), + _portd(portd) { } + +PORTCClass & PORTCClass::operator = (uint8_t const value) { + set_or_clr_out_register(value, 0, _portd, 3); + set_or_clr_out_register(value, 1, _portd, 2); + set_or_clr_out_register(value, 2, _portd, 1); + set_or_clr_out_register(value, 3, _portd, 0); + set_or_clr_out_register(value, 4, _porta, 2); + set_or_clr_out_register(value, 5, _porta, 3); + set_or_clr_out_register(value, 6, _portd, 4); + set_or_clr_out_register(value, 7, _portd, 5); + return *this; +} + +PORTCClass & PORTCClass::operator &= (uint8_t const value) { + clr_out_register(value, 0, _portd, 3); + clr_out_register(value, 1, _portd, 2); + clr_out_register(value, 2, _portd, 1); + clr_out_register(value, 3, _portd, 0); + clr_out_register(value, 4, _porta, 2); + clr_out_register(value, 5, _porta, 3); + clr_out_register(value, 6, _portd, 4); + clr_out_register(value, 7, _portd, 5); + return *this; +} + +PORTCClass & PORTCClass::operator |= (uint8_t const value) { + set_out_register(value, 0, _portd, 3); + set_out_register(value, 1, _portd, 2); + set_out_register(value, 2, _portd, 1); + set_out_register(value, 3, _portd, 0); + set_out_register(value, 4, _porta, 2); + set_out_register(value, 5, _porta, 3); + set_out_register(value, 6, _portd, 4); + set_out_register(value, 7, _portd, 5); + return *this; +} + +/*****************************************************************************/ + +DDRDClass::DDRDClass(PORT_t * porta, PORT_t * portb, PORT_t * portc, PORT_t * portf) +: _porta(porta), + _portb(portb), + _portc(portc), + _portf(portf) { } + +DDRDClass & DDRDClass::operator = (uint8_t const value) { + set_or_clr_dir_register(value, 0, _portc, 4); + set_or_clr_dir_register(value, 1, _portc, 5); + set_or_clr_dir_register(value, 2, _porta, 0); + set_or_clr_dir_register(value, 3, _portf, 5); + set_or_clr_dir_register(value, 4, _portc, 6); + set_or_clr_dir_register(value, 5, _portb, 2); + set_or_clr_dir_register(value, 6, _portf, 4); + set_or_clr_dir_register(value, 7, _porta, 1); + return *this; +} + +DDRDClass & DDRDClass::operator &= (uint8_t const value) { + clr_dir_register(value, 0, _portc, 4); + clr_dir_register(value, 1, _portc, 5); + clr_dir_register(value, 2, _porta, 0); + clr_dir_register(value, 3, _portf, 5); + clr_dir_register(value, 4, _portc, 6); + clr_dir_register(value, 5, _portb, 2); + clr_dir_register(value, 6, _portf, 4); + clr_dir_register(value, 7, _porta, 1); + return *this; +} + +DDRDClass & DDRDClass::operator |= (uint8_t const value) { + set_dir_register(value, 0, _portc, 4); + set_dir_register(value, 1, _portc, 5); + set_dir_register(value, 2, _porta, 0); + set_dir_register(value, 3, _portf, 5); + set_dir_register(value, 4, _portc, 6); + set_dir_register(value, 5, _portb, 2); + set_dir_register(value, 6, _portf, 4); + set_dir_register(value, 7, _porta, 1); + return *this; +} + +PORTDClass::PORTDClass(PORT_t * porta, PORT_t * portb, PORT_t * portc, PORT_t * portf) +: _porta(porta), + _portb(portb), + _portc(portc), + _portf(portf) { } + +PORTDClass & PORTDClass::operator = (uint8_t const value) { + set_or_clr_out_register(value, 0, _portc, 4); + set_or_clr_out_register(value, 1, _portc, 5); + set_or_clr_out_register(value, 2, _porta, 0); + set_or_clr_out_register(value, 3, _portf, 5); + set_or_clr_out_register(value, 4, _portc, 6); + set_or_clr_out_register(value, 5, _portb, 2); + set_or_clr_out_register(value, 6, _portf, 4); + set_or_clr_out_register(value, 7, _porta, 1); + return *this; +} + +PORTDClass & PORTDClass::operator &= (uint8_t const value) { + clr_out_register(value, 0, _portc, 4); + clr_out_register(value, 1, _portc, 5); + clr_out_register(value, 2, _porta, 0); + clr_out_register(value, 3, _portf, 5); + clr_out_register(value, 4, _portc, 6); + clr_out_register(value, 5, _portb, 2); + clr_out_register(value, 6, _portf, 4); + clr_out_register(value, 7, _porta, 1); + return *this; +} + +PORTDClass & PORTDClass::operator |= (uint8_t const value) { + set_out_register(value, 0, _portc, 4); + set_out_register(value, 1, _portc, 5); + set_out_register(value, 2, _porta, 0); + set_out_register(value, 3, _portf, 5); + set_out_register(value, 4, _portc, 6); + set_out_register(value, 5, _portb, 2); + set_out_register(value, 6, _portf, 4); + set_out_register(value, 7, _porta, 1); + return *this; +} + +#endif /* #ifdef AVR_NANO_4809_328MODE */ diff --git a/cores/arduino/NANO_compat.h b/cores/arduino/NANO_compat.h new file mode 100644 index 00000000..9351ef2f --- /dev/null +++ b/cores/arduino/NANO_compat.h @@ -0,0 +1,138 @@ +/* + Copyright (c) 2019 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once +#include + +#ifdef AVR_NANO_4809_328MODE + +/* + ARDUINO PIN ATMEGA 328 ATMEGA 4809 + D0 PD0 PC4 (TX1) + D1 PD1 PC5 (RX1) + D2 PD2 PA0 + D3 PD3 PF5 + D4 PD4 PC6 + D5 PD5 PB2 + D6 PD6 PF4 + D7 PD7 PA1 + D8 PB0 PE3 + D9 PB1 PB0 + D10 PB2 PB1 + D11 PB3 PE0 (MOSI) + D12 PB4 PE1 (MISO) + D13 PB5 PE2 (SCK) + A0 PC0 PD3 + A1 PC1 PD2 + A2 PC2 PD1 + A3 PC3 PD0 + A4 PC4 PA2 (SDA) + A5 PC5 PA3 (SCL) + A6 ADC6 PD4 + A7 ADC7 PD5 +*/ + +class DDRBClass { + public: + DDRBClass(PORT_t * portb, PORT_t * porte); + + DDRBClass & operator = (uint8_t const value); + DDRBClass & operator &= (uint8_t const value); + DDRBClass & operator |= (uint8_t const value); + private: + PORT_t * _portb , * _porte; +}; + +class PORTBClass { + public: + PORTBClass(PORT_t * portb, PORT_t * porte); + + PORTBClass & operator = (uint8_t const value); + PORTBClass & operator &= (uint8_t const value); + PORTBClass & operator |= (uint8_t const value); + private: + PORT_t * _portb , * _porte; +}; + +/*****************************************************************************/ + +class DDRCClass { + public: + DDRCClass(PORT_t * porta, PORT_t * portd); + + DDRCClass & operator = (uint8_t const value); + DDRCClass & operator &= (uint8_t const value); + DDRCClass & operator |= (uint8_t const value); + private: + PORT_t * _porta , * _portd; +}; + +class PORTCClass { + public: + PORTCClass(PORT_t * porta, PORT_t * portd); + + PORTCClass & operator = (uint8_t const value); + PORTCClass & operator &= (uint8_t const value); + PORTCClass & operator |= (uint8_t const value); + private: + PORT_t * _porta , * _portd; +}; + +/*****************************************************************************/ + +class DDRDClass { + public: + DDRDClass(PORT_t * porta, PORT_t * portb, PORT_t * portc, PORT_t * portf); + + DDRDClass & operator = (uint8_t const value); + DDRDClass & operator &= (uint8_t const value); + DDRDClass & operator |= (uint8_t const value); + private: + PORT_t * _porta, * _portb , * _portc, * _portf; +}; + +class PORTDClass { + public: + PORTDClass(PORT_t * porta, PORT_t * portb, PORT_t * portc, PORT_t * portf); + + PORTDClass & operator = (uint8_t const value); + PORTDClass & operator &= (uint8_t const value); + PORTDClass & operator |= (uint8_t const value); + private: + PORT_t * _porta, * _portb , * _portc, * _portf; +}; + +/*****************************************************************************/ + +#ifndef HOST_BUILD + #undef DDRB + #undef PORTB + #undef DDRC + #undef PORTC + #undef DDRD + #undef PORTD + + extern DDRBClass DDRB; + extern PORTBClass PORTB; + extern DDRCClass DDRC; + extern PORTCClass PORTC; + extern DDRDClass DDRD; + extern PORTDClass PORTD; +#endif /* #ifndef HOST_BUILD */ + +#endif /* #ifdef AVR_NANO_4809_328MODE */ \ No newline at end of file diff --git a/cores/arduino/UART.cpp b/cores/arduino/UART.cpp index fa8098c4..473f59b9 100644 --- a/cores/arduino/UART.cpp +++ b/cores/arduino/UART.cpp @@ -88,7 +88,6 @@ void serialEventRun(void) void UartClass::_tx_data_empty_irq(void) { // Check if tx buffer already empty. - // This interrupt-handler can be called "manually" from flush(); if (_tx_buffer_head == _tx_buffer_tail) { // Buffer empty, so disable "data register empty" interrupt (*_hwserial_module).CTRLA &= (~USART_DREIE_bm); @@ -107,8 +106,6 @@ void UartClass::_tx_data_empty_irq(void) (*_hwserial_module).TXDATAL = c; - while(!((*_hwserial_module).STATUS & USART_DREIF_bm)); - if (_tx_buffer_head == _tx_buffer_tail) { // Buffer empty, so disable "data register empty" interrupt (*_hwserial_module).CTRLA &= (~USART_DREIE_bm); @@ -121,6 +118,23 @@ void UartClass::_tx_data_empty_irq(void) } } +// To invoke data empty "interrupt" via a call, use this method +void UartClass::_poll_tx_data_empty(void) +{ + if ( (!(SREG & CPU_I_bm)) || (!((*_hwserial_module).CTRLA & USART_DREIE_bm)) ) { + // Interrupts are disabled either globally or for data register empty, + // so we'll have to poll the "data register empty" flag ourselves. + // If it is set, pretend an interrupt has happened and call the handler + //to free up space for us. + + // Invoke interrupt handler only if conditions data register is empty + if ((*_hwserial_module).STATUS & USART_DREIF_bm) { + _tx_data_empty_irq(); + } + } + // In case interrupts are enabled, the interrupt routine will be invoked by itself +} + // Public Methods ////////////////////////////////////////////////////////////// void UartClass::begin(unsigned long baud, uint16_t config) @@ -140,23 +154,15 @@ void UartClass::begin(unsigned long baud, uint16_t config) uint8_t oldSREG = SREG; cli(); - baud_setting = (((8 * F_CPU_CORRECTED) / baud) + 1) / 2; + baud_setting = (((8 * F_CPU) / baud) + 1) / 2; // Disable CLK2X (*_hwserial_module).CTRLB &= (~USART_RXMODE_CLK2X_gc); (*_hwserial_module).CTRLB |= USART_RXMODE_NORMAL_gc; _written = false; - //Set up the rx pin - pinMode(_hwserial_rx_pin, INPUT_PULLUP); - - //Set up the tx pin - digitalWrite(_hwserial_tx_pin, HIGH); - pinMode(_hwserial_tx_pin, OUTPUT); - int8_t sigrow_val = SIGROW.OSC16ERR5V; - baud_setting *= (1024 + sigrow_val); - baud_setting /= (1024 - abs(sigrow_val)); + baud_setting += (baud_setting * sigrow_val) / 1024; // assign the baud_setting, a.k.a. BAUD (USART Baud Rate Register) (*_hwserial_module).BAUD = (int16_t) baud_setting; @@ -169,6 +175,13 @@ void UartClass::begin(unsigned long baud, uint16_t config) (*_hwserial_module).CTRLA |= USART_RXCIE_bm; + //Set up the rx pin + pinMode(_hwserial_rx_pin, INPUT_PULLUP); + + //Set up the tx pin + digitalWrite(_hwserial_tx_pin, HIGH); + pinMode(_hwserial_tx_pin, OUTPUT); + // Restore SREG content SREG = oldSREG; } @@ -253,9 +266,7 @@ void UartClass::flush() // If interrupts are globally disabled or the and DR empty interrupt is disabled, // poll the "data register empty" interrupt flag to prevent deadlock - if ( (!(SREG & CPU_I_bm)) || (!((*_hwserial_module).CTRLA & USART_DREIE_bm)) ) { - _tx_data_empty_irq(); - } + _poll_tx_data_empty(); } // If we get here, nothing is queued anymore (DREIE is disabled) and // the hardware finished transmission (TXCIF is set). @@ -294,18 +305,9 @@ size_t UartClass::write(uint8_t c) tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE; //If the output buffer is full, there's nothing for it other than to - //wait for the interrupt handler to empty it a bit + //wait for the interrupt handler to empty it a bit (or emulate interrupts) while (i == _tx_buffer_tail) { - if ( ( !(SREG & CPU_I_bm) ) || ( !((*_hwserial_module).CTRLA & USART_DREIE_bm) ) ) { - // Interrupts are disabled either globally or for data register empty, - // so we'll have to poll the "data register empty" flag ourselves. - // If it is set, pretend an interrupt has happened and call the handler - //to free up space for us. - - _tx_data_empty_irq(); - } else { - // nop, the interrupt handler will free up space for us - } + _poll_tx_data_empty(); } _tx_buffer[_tx_buffer_head] = c; diff --git a/cores/arduino/UART.h b/cores/arduino/UART.h index ef966e9f..797ae932 100644 --- a/cores/arduino/UART.h +++ b/cores/arduino/UART.h @@ -27,6 +27,8 @@ #include "api/HardwareSerial.h" #include "pins_arduino.h" +using namespace arduino; + // Define constants and variables for buffering incoming serial data. We're // using a ring buffer (I think), in which head is the index of the location // to which to write the next incoming character and tail is the index of the @@ -164,11 +166,16 @@ class UartClass : public HardwareSerial inline size_t write(unsigned int n) { return write((uint8_t)n); } inline size_t write(int n) { return write((uint8_t)n); } using Print::write; // pull in write(str) and write(buf, size) from Print - operator bool() { return true; } + explicit operator bool() { return true; } + + void bind(UartClass& ser) {bound = &ser; } // Interrupt handlers - Not intended to be called externally inline void _rx_complete_irq(void); void _tx_data_empty_irq(void); + private: + void _poll_tx_data_empty(void); + UartClass* bound = NULL; }; #if defined(HWSERIAL0) diff --git a/cores/arduino/UART0.cpp b/cores/arduino/UART0.cpp index b8f1f34c..522b6844 100644 --- a/cores/arduino/UART0.cpp +++ b/cores/arduino/UART0.cpp @@ -51,7 +51,7 @@ ISR(HWSERIAL0_DRE_VECTOR) Serial._tx_data_empty_irq(); } #else -#error "Don't know what the Data Received interrupt vector is called for Serial" +#error "Don't know what the Data Register Empty interrupt vector is called for Serial" #endif #if defined(HWSERIAL0) diff --git a/cores/arduino/UART_private.h b/cores/arduino/UART_private.h index 0bca2d3c..2fa93694 100644 --- a/cores/arduino/UART_private.h +++ b/cores/arduino/UART_private.h @@ -66,6 +66,9 @@ void UartClass::_rx_complete_irq(void) _rx_buffer[_rx_buffer_head] = c; _rx_buffer_head = i; } + if (bound != NULL) { + bound->write(c); + } } else { // Parity error, read byte but discard it (*_hwserial_module).RXDATAL; diff --git a/cores/arduino/UNO_compat.cpp b/cores/arduino/UNO_compat.cpp index e1511e02..076c75b5 100644 --- a/cores/arduino/UNO_compat.cpp +++ b/cores/arduino/UNO_compat.cpp @@ -13,10 +13,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef UNO_WIFI_REV2_328MODE - #include "UNO_compat.h" +#ifdef UNO_WIFI_REV2_328MODE + #warning "ATMEGA328 registers emulation is enabled. You may encounter some speed issue. Please consider to disable it in the Tools menu" PORTBClass PORTB; @@ -27,4 +27,4 @@ DDRBClass DDRB; DDRCClass DDRC; DDRDClass DDRD; -#endif \ No newline at end of file +#endif /* #ifdef UNO_WIFI_REV2_328MODE */ \ No newline at end of file diff --git a/cores/arduino/UNO_compat.h b/cores/arduino/UNO_compat.h index 1aad58df..02f5a7fb 100644 --- a/cores/arduino/UNO_compat.h +++ b/cores/arduino/UNO_compat.h @@ -16,6 +16,8 @@ #pragma once #include "Arduino.h" +#ifdef UNO_WIFI_REV2_328MODE + /* ARDUINO PIN ATMEGA 328 ATMEGA 4809 0 PD0 PC5 @@ -283,3 +285,5 @@ extern PORTDClass PORTD; extern DDRBClass DDRB; extern DDRCClass DDRC; extern DDRDClass DDRD; + +#endif /* #ifdef UNO_WIFI_REV2_328MODE */ \ No newline at end of file diff --git a/cores/arduino/WInterrupts.c b/cores/arduino/WInterrupts.cpp similarity index 87% rename from cores/arduino/WInterrupts.c rename to cores/arduino/WInterrupts.cpp index 64cd9ae6..55155f4c 100644 --- a/cores/arduino/WInterrupts.c +++ b/cores/arduino/WInterrupts.cpp @@ -32,9 +32,10 @@ #include "wiring_private.h" -static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS]; +static volatile voidFuncPtrParam intFunc[EXTERNAL_NUM_INTERRUPTS]; +static void* args[EXTERNAL_NUM_INTERRUPTS]; -void attachInterrupt(uint8_t pin, void (*userFunc)(void), PinStatus mode) { +void attachInterruptParam(pin_size_t pin, void (*userFunc)(void*), PinStatus mode, void* params) { /* Get bit position and check pin validity */ uint8_t bit_pos = digitalPinToBitPosition(pin); @@ -46,24 +47,27 @@ void attachInterrupt(uint8_t pin, void (*userFunc)(void), PinStatus mode) { /* Check interrupt number and apply function pointer to correct array index */ if(interruptNum < EXTERNAL_NUM_INTERRUPTS) { intFunc[interruptNum] = userFunc; + args[interruptNum] = params; // Configure the interrupt mode (trigger on low input, any change, rising // edge, or falling edge). The mode constants were chosen to correspond // to the configuration bits in the hardware register, so we simply apply // the setting in the pin control register + int isc_mode; + switch (mode) { case CHANGE: - mode = PORT_ISC_BOTHEDGES_gc; + isc_mode = PORT_ISC_BOTHEDGES_gc; break; case FALLING: - mode = PORT_ISC_FALLING_gc; + isc_mode = PORT_ISC_FALLING_gc; break; case RISING: - mode = PORT_ISC_RISING_gc; + isc_mode = PORT_ISC_RISING_gc; break; case LOW: - mode = PORT_ISC_LEVEL_gc; + isc_mode = PORT_ISC_LEVEL_gc; break; default: // AVR doesn't support level triggered interrupts @@ -80,10 +84,14 @@ void attachInterrupt(uint8_t pin, void (*userFunc)(void), PinStatus mode) { *pin_ctrl_reg &= ~(PORT_ISC_gm); /* Apply ISC setting */ - *pin_ctrl_reg |= mode; + *pin_ctrl_reg |= isc_mode; } } +void attachInterrupt(uint8_t pin, void (*userFunc)(void), PinStatus mode) { + attachInterruptParam(pin, (voidFuncPtrParam)userFunc, mode, NULL); +} + void detachInterrupt(uint8_t pin) { /* Get bit position and check pin validity */ uint8_t bit_pos = digitalPinToBitPosition(pin); @@ -127,7 +135,7 @@ static void port_interrupt_handler(uint8_t port) { if(intFunc[interrupt_num] != 0){ /* Call function */ - intFunc[interrupt_num](); + intFunc[interrupt_num](args[interrupt_num]); } } bit_pos++; diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index c5a715b5..36ddc680 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -23,7 +23,6 @@ */ #include "wiring_private.h" -#include "pins_arduino.h" #include "Arduino.h" uint8_t analog_reference = DEFAULT; @@ -146,6 +145,8 @@ void analogWrite(uint8_t pin, int val) uint16_t* timer_cmp_out; TCB_t *timer_B; + uint8_t savedSREG; + /* Find out Port and Pin to correctly handle port mux, and timer. */ switch (digital_pin_timer) { @@ -154,7 +155,10 @@ void analogWrite(uint8_t pin, int val) timer_cmp_out = ((uint16_t*) (&TCA0.SINGLE.CMP0BUF)) + bit_pos; /* Configure duty cycle for correct compare channel */ - (*timer_cmp_out) = (val); + savedSREG = SREG; + cli(); + (*timer_cmp_out) = (val); // non-atomic 16-bit write operation + SREG = savedSREG; /* Enable output on pin */ TCA0.SINGLE.CTRLB |= (1 << (TCA_SINGLE_CMP0EN_bp + bit_pos)); @@ -171,7 +175,12 @@ void analogWrite(uint8_t pin, int val) timer_B = ((TCB_t *)&TCB0 + (digital_pin_timer - TIMERB0)); /* set duty cycle */ - timer_B->CCMPH = val; + // (16-bit read/write operation are non-atomic and use a temporary register) + savedSREG = SREG; + cli(); + timer_B->CCMPL = timer_B->CCMPL; // copy CCMPL into temporary register + timer_B->CCMPH = val; // set CCMPH value + copy temporary register content into CCMPL + SREG = savedSREG; /* Enable Timer Output */ timer_B->CTRLB |= (TCB_CCMPEN_bm); diff --git a/cores/test/CMakeLists.txt b/cores/test/CMakeLists.txt new file mode 100644 index 00000000..bea90144 --- /dev/null +++ b/cores/test/CMakeLists.txt @@ -0,0 +1,49 @@ +########################################################################## + +cmake_minimum_required(VERSION 3.0) + +########################################################################## + +include_directories(include) +include_directories(external/libvireg/include) +include_directories(external/catch/v.2.7.0/include) + +########################################################################## + +set(CMAKE_CXX_STANDARD 11) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +########################################################################## + +add_subdirectory(external/libvireg) # For building libvireg + +########################################################################## + +set(TEST_TARGET testArduinoCore-megaavr) + +set(TEST_SRCS + src/test_main.cpp + ../arduino/NANO_compat.cpp +) + +########################################################################## + +add_definitions(-DHOST_BUILD) +add_definitions(-DAVR_NANO_4809_328MODE) + +########################################################################## + +add_executable(${TEST_TARGET} ${TEST_SRCS}) +target_link_libraries(${TEST_TARGET} vireg) + +########################################################################## + +add_custom_command( + TARGET ${TEST_TARGET} POST_BUILD + COMMENT "Executing unit tests" + COMMAND "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TEST_TARGET}" +) + +########################################################################## diff --git a/cores/test/README.md b/cores/test/README.md new file mode 100644 index 00000000..3635c7fd --- /dev/null +++ b/cores/test/README.md @@ -0,0 +1,8 @@ +Compilation/Execution of unit tests +=================================== +Unit tests are automatically executed as a post-build step. +```bash +mkdir build && cd build +cmake .. +make +``` diff --git a/cores/test/external/catch/v.2.7.0/include/catch2/catch.hpp b/cores/test/external/catch/v.2.7.0/include/catch2/catch.hpp new file mode 100644 index 00000000..1850fff1 --- /dev/null +++ b/cores/test/external/catch/v.2.7.0/include/catch2/catch.hpp @@ -0,0 +1,14934 @@ +/* + * Catch v2.7.0 + * Generated: 2019-03-07 21:34:30.252164 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 7 +#define CATCH_VERSION_PATCH 0 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +#if defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#ifdef __clang__ + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if string_view is available and usable +// The check is split apart to work around v140 (VS2015) preprocessor issue... +#if defined(__has_include) +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if optional is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +//////////////////////////////////////////////////////////////////////////////// +// Check if variant is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 +# include +# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# define CATCH_CONFIG_NO_CPP17_VARIANT +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__clang__) && (__clang_major__ < 8) +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +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_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { + public: + using size_type = std::size_t; + + private: + friend struct StringRefTestAccess; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + static constexpr char const* const s_empty = ""; + + public: // construction/ assignment + StringRef() noexcept + : StringRef( s_empty, 0 ) + {} + + StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + + StringRef( char const* rawChars ) noexcept; + + StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + ~StringRef() noexcept { + delete[] m_data; + } + + auto operator = ( StringRef const &other ) noexcept -> StringRef& { + delete[] m_data; + m_data = nullptr; + m_start = other.m_start; + m_size = other.m_size; + return *this; + } + + operator std::string() const; + + void swap( StringRef& other ) noexcept; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; + + auto operator[] ( size_type index ) const noexcept -> char; + + public: // named queries + auto empty() const noexcept -> bool { + return m_size == 0; + } + auto size() const noexcept -> size_type { + return m_size; + } + + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; + + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; + + // Returns the current start pointer. + // Note that the pointer can change when if the StringRef is a substring + auto currentData() const noexcept -> char const*; + + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + }; + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } + +} // namespace Catch + +inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_type_traits.hpp + + +#include + +namespace Catch{ + +#ifdef CATCH_CPP17_OR_GREATER + template + inline constexpr auto is_unique = std::true_type{}; + + template + inline constexpr auto is_unique = std::bool_constant< + (!std::is_same_v && ...) && is_unique + >{}; +#else + +template +struct is_unique : std::true_type{}; + +template +struct is_unique : std::integral_constant +::value + && is_unique::value + && is_unique::value +>{}; + +#endif +} + +// end catch_type_traits.hpp +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__ +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +// MSVC is adding extra space and needs more calls to properly remove () +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__ +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__) +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LIST(types) Catch::TypeList + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(types)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,INTERNAL_CATCH_REMOVE_PARENS(types)) + +// end catch_preprocessor.hpp +// start catch_meta.hpp + + +#include + +namespace Catch { +template< typename... > +struct TypeList {}; + +template< typename... > +struct append; + +template< template class L1 + , typename...E1 + , template class L2 + , typename...E2 +> +struct append< L1, L2 > { + using type = L1; +}; + +template< template class L1 + , typename...E1 + , template class L2 + , typename...E2 + , typename...Rest +> +struct append< L1, L2, Rest...> { + using type = typename append< L1, Rest... >::type; +}; + +template< template class + , typename... +> +struct rewrap; + +template< template class Container + , template class List + , typename...elems +> +struct rewrap> { + using type = TypeList< Container< elems... > >; +}; + +template< template class Container + , template class List + , class...Elems + , typename...Elements> + struct rewrap, Elements...> { + using type = typename append>, typename rewrap::type>::type; +}; + +template< template class...Containers > +struct combine { + template< typename...Types > + struct with_types { + template< template class Final > + struct into { + using type = typename append, typename rewrap::type...>::type; + }; + }; +}; + +template +struct always_false : std::false_type {}; + +} // namespace Catch + +// end catch_meta.hpp +namespace Catch { + +template +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); +public: + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} + + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); + } +}; + +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; + +template +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod( testAsMethod ); +} + +struct NameAndTags { + NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept; + StringRef name; + StringRef tags; +}; + +struct AutoReg : NonCopyable { + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept; + ~AutoReg(); +}; + +} // end namespace Catch + +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... ) \ + template \ + static void TestName() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test(); \ + }; \ + } \ + template \ + void TestName::test() +#endif + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template \ + static void TestFunc();\ + namespace {\ + template \ + struct TestName{\ + template \ + TestName(Ts...names){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \ + }\ + };\ + INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + static void TestFunc() + +#if defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case"); +#else +#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case"); +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, ...)\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + TestName(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\ + return 0;\ + }(); + + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, TmplTypes, TypesList) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template static void TestFuncName(); \ + namespace { \ + template \ + struct TestName { \ + TestName() { \ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...) \ + int index = 0; \ + using expander = int[]; \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */\ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ + using TestInit = Catch::combine \ + ::with_types::into::type; \ + TestInit(); \ + return 0; \ + }(); \ + } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + static void TestFuncName() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ),Name,Tags,__VA_ARGS__) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test();\ + };\ + template \ + struct TestNameClass{\ + template \ + TestNameClass(Ts...names){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \ + }\ + };\ + INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\ + template \ + void TestName::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, TmplTypes, TypesList)\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test();\ + };\ + namespace {\ + template\ + struct TestNameClass{\ + TestNameClass(){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...)\ + int index = 0;\ + using expander = int[];\ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = Catch::combine\ + ::with_types::into::type;\ + TestInit();\ + return 0;\ + }(); \ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + void TestName::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, __VA_ARGS__ ) ) +#endif + +// end catch_test_registry.h +// start catch_capture.hpp + +// start catch_assertionhandler.h + +// start catch_assertioninfo.h + +// start catch_result_type.h + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); + + bool shouldContinueOnFailure( int flags ); + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ); + +} // end namespace Catch + +// end catch_result_type.h +namespace Catch { + + struct AssertionInfo + { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; + }; + +} // end namespace Catch + +// end catch_assertioninfo.h +// start catch_decomposer.h + +// start catch_tostring.h + +#include +#include +#include +#include +// start catch_stream.h + +#include +#include +#include + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + std::ostream& clog(); + + class StringRef; + + struct IStream { + virtual ~IStream(); + virtual std::ostream& stream() const = 0; + }; + + auto makeStream( StringRef const &filename ) -> IStream const*; + + class ReusableStringStream { + std::size_t m_index; + std::ostream* m_oss; + public: + ReusableStringStream(); + ~ReusableStringStream(); + + auto str() const -> std::string; + + template + auto operator << ( T const& value ) -> ReusableStringStream& { + *m_oss << value; + return *this; + } + auto get() -> std::ostream& { return *m_oss; } + }; +} + +// end catch_stream.h + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +#include +#endif + +#ifdef __OBJC__ +// start catch_objc_arc.hpp + +#import + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +// end catch_objc_arc.hpp +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless +#endif + +namespace Catch { + namespace Detail { + + extern const std::string unprintableString; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); + + template + static auto test(...)->std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; + + template + std::string convertUnknownEnumToString( E e ); + + template + typename std::enable_if< + !std::is_enum::value && !std::is_base_of::value, + std::string>::type convertUnstreamable( T const& ) { + return Detail::unprintableString; + } + template + typename std::enable_if< + !std::is_enum::value && std::is_base_of::value, + std::string>::type convertUnstreamable(T const& ex) { + return ex.what(); + } + + template + typename std::enable_if< + std::is_enum::value + , std::string>::type convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + } + +#if defined(_MANAGED) + //! Convert a CLR string to a utf8 std::string + template + std::string clrReferenceToString( T^ ref ) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr p = &bytes[0]; + return std::string(reinterpret_cast(p), bytes->Length); + } +#endif + + } // namespace Detail + + // If we decide for C++14, change these to enable_if_ts + template + struct StringMaker { + template + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type + convert(const Fake& value) { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } + + template + static + typename std::enable_if::value, std::string>::type + convert( const Fake& value ) { +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) + return Detail::convertUnstreamable(value); +#else + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif + } + }; + + namespace Detail { + + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template + std::string stringify(const T& e) { + return ::Catch::StringMaker::type>::type>::convert(e); + } + + template + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast::type>(e)); + } + +#if defined(_MANAGED) + template + std::string stringify( T^ e ) { + return ::Catch::StringMaker::convert(e); + } +#endif + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker { + static std::string convert(const std::string& str); + }; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::string_view str); + }; +#endif + + template<> + struct StringMaker { + static std::string convert(char const * str); + }; + template<> + struct StringMaker { + static std::string convert(char * str); + }; + +#ifdef CATCH_CONFIG_WCHAR + template<> + struct StringMaker { + static std::string convert(const std::wstring& wstr); + }; + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::wstring_view str); + }; +# endif + + template<> + struct StringMaker { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t * str); + }; +#endif + + // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, + // while keeping string semantics? + template + struct StringMaker { + static std::string convert(char const* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(signed char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); + } + }; + template + struct StringMaker { + static std::string convert(unsigned char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); + } + }; + + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker { + static std::string convert(bool b); + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker { + static std::string convert(float value); + }; + template<> + struct StringMaker { + static std::string convert(double value); + }; + + template + struct StringMaker { + template + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template + struct StringMaker { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + + namespace Detail { + template + std::string rangeToString(InputIterator first, InputIterator last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); + } + } + +#ifdef __OBJC__ + template<> + struct StringMaker { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } + + }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker::convert( nsstring ); + } + + } // namespace Detail +#endif // __OBJC__ + +} // namespace Catch + +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::pair& pair) { + ReusableStringStream rss; + rss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::optional& optional) { + ReusableStringStream rss; + if (optional.has_value()) { + rss << ::Catch::Detail::stringify(*optional); + } else { + rss << "{ }"; + } + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + template + struct StringMaker> { + static std::string convert(const std::tuple& tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter>::print(tuple, rss.get()); + rss << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) +#include +namespace Catch { + template<> + struct StringMaker { + static std::string convert(const std::monostate&) { + return "{ }"; + } + }; + + template + struct StringMaker> { + static std::string convert(const std::variant& variant) { + if (variant.valueless_by_exception()) { + return "{valueless variant}"; + } else { + return std::visit( + [](const auto& value) { + return ::Catch::Detail::stringify(value); + }, + variant + ); + } + } + }; +} +#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER + +namespace Catch { + struct not_this_one {}; // Tag type for detecting which begin/ end are being selected + + // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace + using std::begin; + using std::end; + + not_this_one begin( ... ); + not_this_one end( ... ); + + template + struct is_range { + static const bool value = + !std::is_same())), not_this_one>::value && + !std::is_same())), not_this_one>::value; + }; + +#if defined(_MANAGED) // Managed types are never ranges + template + struct is_range { + static const bool value = false; + }; +#endif + + template + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + } + + // Handle vector specially + template + std::string rangeToString( std::vector const& v ) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for( bool b : v ) { + if( first ) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify( b ); + } + rss << " }"; + return rss.str(); + } + + template + struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; + + template + struct StringMaker { + static std::string convert(T const(&arr)[SZ]) { + return rangeToString(arr); + } + }; + +} // namespace Catch + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include +#include +#include + +namespace Catch { + +template +struct ratio_string { + static std::string symbol(); +}; + +template +std::string ratio_string::symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); +} +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; + + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const& duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; + + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_tostring.h +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#pragma warning(disable:4800) // Forcing result to true or false +#endif + +namespace Catch { + + struct ITransientExpression { + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } + virtual void streamReconstructedExpression( std::ostream &os ) const = 0; + + ITransientExpression( bool isBinaryExpression, bool result ) + : m_isBinaryExpression( isBinaryExpression ), + m_result( result ) + {} + + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); + + bool m_isBinaryExpression; + bool m_result; + + }; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); + + template + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + formatReconstructedExpression + ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + } + + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : ITransientExpression{ true, comparisonResult }, + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} + + template + auto operator && ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator || ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator == ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator != ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator > ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator < ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator >= ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator <= ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + }; + + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + os << Catch::Detail::stringify( m_lhs ); + } + + public: + explicit UnaryExpr( LhsT lhs ) + : ITransientExpression{ false, static_cast(lhs) }, + m_lhs( lhs ) + {} + }; + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } + template + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + template + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + + template + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } + template + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + template + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + + template + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + + template + auto operator == ( RhsT const& rhs ) -> BinaryExpr const { + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; + } + auto operator == ( bool rhs ) -> BinaryExpr const { + return { m_lhs == rhs, m_lhs, "==", rhs }; + } + + template + auto operator != ( RhsT const& rhs ) -> BinaryExpr const { + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; + } + auto operator != ( bool rhs ) -> BinaryExpr const { + return { m_lhs != rhs, m_lhs, "!=", rhs }; + } + + template + auto operator > ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs > rhs), m_lhs, ">", rhs }; + } + template + auto operator < ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs < rhs), m_lhs, "<", rhs }; + } + template + auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs >= rhs), m_lhs, ">=", rhs }; + } + template + auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs <= rhs), m_lhs, "<=", rhs }; + } + + template + auto operator && ( RhsT const& ) -> BinaryExpr const { + static_assert(always_false::value, + "operator&& is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator || ( RhsT const& ) -> BinaryExpr const { + static_assert(always_false::value, + "operator|| is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr{ m_lhs }; + } + }; + + void handleExpression( ITransientExpression const& expr ); + + template + void handleExpression( ExprLhs const& expr ) { + handleExpression( expr.makeUnaryExpr() ); + } + + struct Decomposer { + template + auto operator <= ( T const& lhs ) -> ExprLhs { + return ExprLhs{ lhs }; + } + + auto operator <=( bool value ) -> ExprLhs { + return ExprLhs{ value }; + } + }; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_decomposer.h +// start catch_interfaces_capture.h + +#include + +namespace Catch { + + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct MessageBuilder; + struct Counts; + struct BenchmarkInfo; + struct BenchmarkStats; + struct AssertionReaction; + struct SourceLineInfo; + + struct ITransientExpression; + struct IGeneratorTracker; + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + + virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; + + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; + + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0; + + virtual void handleFatalErrorCondition( StringRef message ) = 0; + + virtual void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) = 0; + virtual void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) = 0; + virtual void handleIncomplete + ( AssertionInfo const& info ) = 0; + virtual void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + }; + + IResultCapture& getResultCapture(); +} + +// end catch_interfaces_capture.h +namespace Catch { + + struct TestFailureException{}; + struct AssertionResultData; + struct IResultCapture; + class RunContext; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ); + LazyExpression( LazyExpression const& other ); + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const; + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; + + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture& m_resultCapture; + + public: + AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler() { + if ( !m_completed ) { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + } + + template + void handleExpr( ExprLhs const& expr ) { + handleExpr( expr.makeUnaryExpr() ); + } + void handleExpr( ITransientExpression const& expr ); + + void handleMessage(ResultWas::OfType resultType, StringRef const& message); + + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); + + void complete(); + void setCompleted(); + + // query + auto allowThrows() const -> bool; + }; + + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ); + +} // namespace Catch + +// end catch_assertionhandler.h +// start catch_message.h + +#include +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator == ( MessageInfo const& other ) const; + bool operator < ( MessageInfo const& other ) const; + private: + static unsigned int globalCount; + }; + + struct MessageStream { + + template + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; + }; + + struct MessageBuilder : MessageStream { + MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ); + + template + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + }; + + class ScopedMessage { + public: + explicit ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage& duplicate ) = delete; + ScopedMessage( ScopedMessage&& old ); + ~ScopedMessage(); + + MessageInfo m_info; + bool m_moved; + }; + + class Capturer { + std::vector m_messages; + IResultCapture& m_resultCapture = getResultCapture(); + size_t m_captured = 0; + public: + Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); + ~Capturer(); + + void captureValue( size_t index, std::string const& value ); + + template + void captureValues( size_t index, T const& value ) { + captureValue( index, Catch::Detail::stringify( value ) ); + } + + template + void captureValues( size_t index, T const& value, Ts const&... values ) { + captureValue( index, Catch::Detail::stringify(value) ); + captureValues( index+1, values... ); + } + }; + +} // end namespace Catch + +// end catch_message.h +#if !defined(CATCH_CONFIG_DISABLE) + +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ +#else + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + +#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +#define INTERNAL_CATCH_TRY +#define INTERNAL_CATCH_CATCH( capturer ) + +#else // CATCH_CONFIG_FAST_COMPILE + +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } + +#endif + +#define INTERNAL_CATCH_REACT( handler ) handler.complete(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(expr); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#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, ... ) \ + auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ + varName.captureValues( 0, __VA_ARGS__ ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( macroName, log ) \ + 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 ) + +/////////////////////////////////////////////////////////////////////////////// +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_capture.hpp +// start catch_section.h + +// start catch_section_info.h + +// start catch_totals.h + +#include + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::size_t total() const; + bool allPassed() const; + bool allOk() const; + + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + int error = 0; + Counts assertions; + Counts testCases; + }; +} + +// end catch_totals.h +#include + +namespace Catch { + + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ); + + // Deprecated + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& ) : SectionInfo( _lineInfo, _name ) {} + + std::string name; + std::string description; // !Deprecated: this will always be empty + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +// end catch_section_info.h +// start catch_timer.h + +#include + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; + + class Timer { + uint64_t m_nanoseconds = 0; + public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; + }; + +} // namespace Catch + +// end catch_timer.h +#include + +namespace Catch { + + class Section : NonCopyable { + public: + Section( SectionInfo const& info ); + ~Section(); + + // This indicates whether the section should be executed or not + explicit operator bool() const; + + private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +// end catch_section.h +// start catch_benchmark.h + +#include +#include + +namespace Catch { + + class BenchmarkLooper { + + std::string m_name; + std::size_t m_count = 0; + std::size_t m_iterationsToRun = 1; + uint64_t m_resolution; + Timer m_timer; + + static auto getResolution() -> uint64_t; + public: + // Keep most of this inline as it's on the code path that is being timed + BenchmarkLooper( StringRef name ) + : m_name( name ), + m_resolution( getResolution() ) + { + reportStart(); + m_timer.start(); + } + + explicit operator bool() { + if( m_count < m_iterationsToRun ) + return true; + return needsMoreIterations(); + } + + void increment() { + ++m_count; + } + + void reportStart(); + auto needsMoreIterations() -> bool; + }; + +} // end namespace Catch + +#define BENCHMARK( name ) \ + for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) + +// end catch_benchmark.h +// start catch_interfaces_exception.h + +// start catch_interfaces_registry_hub.h + +#include +#include + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + struct ITagAliasRegistry; + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; + virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; + }; + + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +// end catch_interfaces_registry_hub.h +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ + static std::string translatorName( signature ) +#endif + +#include +#include +#include + +namespace Catch { + using exceptionTranslateFunction = std::string(*)(); + + struct IExceptionTranslator; + using ExceptionTranslators = std::vector>; + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { + try { + if( it == itEnd ) + std::rethrow_exception(std::current_exception()); + else + return (*it)->translate( it+1, itEnd ); + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// end catch_interfaces_exception.h +// start catch_approx.h + +#include + +namespace Catch { +namespace Detail { + + class Approx { + private: + bool equalityComparisonImpl(double other) const; + // Validates the new margin (margin >= 0) + // out-of-line to avoid including stdexcept in the header + void setMargin(double margin); + // Validates the new epsilon (0 < epsilon < 1) + // out-of-line to avoid including stdexcept in the header + void setEpsilon(double epsilon); + + public: + explicit Approx ( double value ); + + static Approx custom(); + + Approx operator-() const; + + template ::value>::type> + Approx operator()( T const& value ) { + Approx approx( static_cast(value) ); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } + + template ::value>::type> + explicit Approx( T const& value ): Approx(static_cast(value)) + {} + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T const& lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T const& rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template ::value>::type> + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template ::value>::type> + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val); + Detail::Approx operator "" _a(unsigned long long val); +} // end namespace literals + +template<> +struct StringMaker { + static std::string convert(Catch::Detail::Approx const& value); +}; + +} // end namespace Catch + +// end catch_approx.h +// start catch_string_manip.h + +#include +#include + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; +} + +// end catch_string_manip.h +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +// start catch_capture_matchers.h + +// start catch_matchers.h + +#include +#include + +namespace Catch { +namespace Matchers { + namespace Impl { + + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; + + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + MatcherUntypedBase ( MatcherUntypedBase const& ) = default; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + std::string toString() const; + + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + }; + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif + + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { + + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (!matcher->match(arg)) + return false; + } + return true; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + + template + struct MatchNotOf : MatcherBase { + + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + + bool match( ArgT const& arg ) const override { + return !m_underlyingMatcher.match( arg ); + } + + std::string describe() const override { + return "not " + m_underlyingMatcher.toString(); + } + MatcherBase const& m_underlyingMatcher; + }; + + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } + + } // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_floating.h + +#include +#include + +namespace Catch { +namespace Matchers { + + namespace Floating { + + enum class FloatingPointKind : uint8_t; + + struct WithinAbsMatcher : MatcherBase { + WithinAbsMatcher(double target, double margin); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_margin; + }; + + struct WithinUlpsMatcher : MatcherBase { + WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + int m_ulps; + FloatingPointKind m_type; + }; + + } // namespace Floating + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); + Floating::WithinAbsMatcher WithinAbs(double target, double margin); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.h +// start catch_matchers_generic.hpp + +#include +#include + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { + std::string finalizeDescription(const std::string& desc); +} + +template +class PredicateMatcher : public MatcherBase { + std::function m_predicate; + std::string m_description; +public: + + PredicateMatcher(std::function const& elem, std::string const& descr) + :m_predicate(std::move(elem)), + m_description(Detail::finalizeDescription(descr)) + {} + + bool match( T const& item ) const override { + return m_predicate(item); + } + + std::string describe() const override { + return m_description; + } +}; + +} // namespace Generic + + // The following functions create the actual matcher objects. + // The user has to explicitly specify type to the function, because + // infering std::function is hard (but possible) and + // requires a lot of TMP. + template + Generic::PredicateMatcher Predicate(std::function const& predicate, std::string const& description = "") { + return Generic::PredicateMatcher(predicate, description); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp +// start catch_matchers_string.h + +#include + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + std::string describe() const override; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + + struct RegexMatcher : MatcherBase { + RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); + bool match( std::string const& matchee ) const override; + std::string describe() const override; + + private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_string.h +// start catch_matchers_vector.h + +#include + +namespace Catch { +namespace Matchers { + + namespace Vector { + namespace Detail { + template + size_t count(InputIterator first, InputIterator last, T const& item) { + size_t cnt = 0; + for (; first != last; ++first) { + if (*first == item) { + ++cnt; + } + } + return cnt; + } + template + bool contains(InputIterator first, InputIterator last, T const& item) { + for (; first != last; ++first) { + if (*first == item) { + return true; + } + } + return false; + } + } + + template + struct ContainsElementMatcher : MatcherBase> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector const &v) const override { + for (auto const& el : v) { + if (el == m_comparator) { + return true; + } + } + return false; + } + + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + T const& m_comparator; + }; + + template + struct ContainsMatcher : MatcherBase> { + + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const override { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const& comparator : m_comparator) { + auto present = false; + for (const auto& el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + std::vector const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase> { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const override { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + std::string describe() const override { + return "Equals: " + ::Catch::Detail::stringify( m_comparator ); + } + std::vector const& m_comparator; + }; + + template + struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} + bool match(std::vector const& vec) const override { + // Note: This is a reimplementation of std::is_permutation, + // because I don't want to include inside the common path + if (m_target.size() != vec.size()) { + return false; + } + auto lfirst = m_target.begin(), llast = m_target.end(); + auto rfirst = vec.begin(), rlast = vec.end(); + // Cut common prefix to optimize checking of permuted parts + while (lfirst != llast && *lfirst == *rfirst) { + ++lfirst; ++rfirst; + } + if (lfirst == llast) { + return true; + } + + for (auto mid = lfirst; mid != llast; ++mid) { + // Skip already counted items + if (Detail::contains(lfirst, mid, *mid)) { + continue; + } + size_t num_vec = Detail::count(rfirst, rlast, *mid); + if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) { + return false; + } + } + + return true; + } + + std::string describe() const override { + return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); + } + private: + std::vector const& m_target; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + + template + Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { + return Vector::UnorderedEqualsMatcher(target); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h +namespace Catch { + + template + class MatchExpr : public ITransientExpression { + ArgT const& m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + public: + MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) + : ITransientExpression{ true, matcher.match( arg ) }, + m_arg( arg ), + m_matcher( matcher ), + m_matcherString( matcherString ) + {} + + void streamReconstructedExpression( std::ostream &os ) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify( m_arg ) << ' '; + if( matcherAsString == Detail::unprintableString ) + os << m_matcherString; + else + os << matcherAsString; + } + }; + + using StringMatcher = Matchers::Impl::MatcherBase; + + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); + + template + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr { + return MatchExpr( arg, matcher, matcherString ); + } + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__ ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ex ) { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +// end catch_capture_matchers.h +#endif +// start catch_generators.hpp + +// start catch_interfaces_generatortracker.h + + +#include + +namespace Catch { + + namespace Generators { + class GeneratorUntypedBase { + public: + GeneratorUntypedBase() = default; + virtual ~GeneratorUntypedBase(); + // Attempts to move the generator to the next element + // + // Returns true iff the move succeeded (and a valid element + // can be retrieved). + virtual bool next() = 0; + }; + using GeneratorBasePtr = std::unique_ptr; + + } // namespace Generators + + struct IGeneratorTracker { + virtual ~IGeneratorTracker(); + virtual auto hasGenerator() const -> bool = 0; + virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; + virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; + }; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + template + [[noreturn]] + void throw_exception(Ex const& e) { + throw e; + } +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + [[noreturn]] + void throw_exception(std::exception const& e); +#endif +} // namespace Catch; + +#define CATCH_PREPARE_EXCEPTION( type, msg ) \ + type( ( Catch::ReusableStringStream() << msg ).str() ) +#define CATCH_INTERNAL_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg)) +#define CATCH_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg )) +#define CATCH_RUNTIME_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg )) +#define CATCH_ENFORCE( condition, msg ) \ + do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) + +// end catch_enforce.h +#include +#include +#include + +#include +#include + +namespace Catch { + +class GeneratorException : public std::exception { + const char* const m_msg = ""; + +public: + GeneratorException(const char* msg): + m_msg(msg) + {} + + const char* what() const noexcept override final; +}; + +namespace Generators { + + // !TBD move this into its own location? + namespace pf{ + template + std::unique_ptr make_unique( Args&&... args ) { + return std::unique_ptr(new T(std::forward(args)...)); + } + } + + template + struct IGenerator : GeneratorUntypedBase { + virtual ~IGenerator() = default; + + // Returns the current element of the generator + // + // \Precondition The generator is either freshly constructed, + // or the last call to `next()` returned true + virtual T const& get() const = 0; + using type = T; + }; + + template + class SingleValueGenerator final : public IGenerator { + T m_value; + public: + SingleValueGenerator(T const& value) : m_value( value ) {} + SingleValueGenerator(T&& value) : m_value(std::move(value)) {} + + T const& get() const override { + return m_value; + } + bool next() override { + return false; + } + }; + + template + class FixedValuesGenerator final : public IGenerator { + std::vector m_values; + size_t m_idx = 0; + public: + FixedValuesGenerator( std::initializer_list values ) : m_values( values ) {} + + T const& get() const override { + return m_values[m_idx]; + } + bool next() override { + ++m_idx; + return m_idx < m_values.size(); + } + }; + + template + class GeneratorWrapper final { + std::unique_ptr> m_generator; + public: + GeneratorWrapper(std::unique_ptr> generator): + m_generator(std::move(generator)) + {} + T const& get() const { + return m_generator->get(); + } + bool next() { + return m_generator->next(); + } + }; + + template + GeneratorWrapper value(T&& value) { + return GeneratorWrapper(pf::make_unique>(std::forward(value))); + } + template + GeneratorWrapper values(std::initializer_list values) { + return GeneratorWrapper(pf::make_unique>(values)); + } + + template + class Generators : public IGenerator { + std::vector> m_generators; + size_t m_current = 0; + + void populate(GeneratorWrapper&& generator) { + m_generators.emplace_back(std::move(generator)); + } + void populate(T&& val) { + m_generators.emplace_back(value(std::move(val))); + } + template + void populate(U&& val) { + populate(T(std::move(val))); + } + template + void populate(U&& valueOrGenerator, Gs... moreGenerators) { + populate(std::forward(valueOrGenerator)); + populate(std::forward(moreGenerators)...); + } + + public: + template + Generators(Gs... moreGenerators) { + m_generators.reserve(sizeof...(Gs)); + populate(std::forward(moreGenerators)...); + } + + T const& get() const override { + return m_generators[m_current].get(); + } + + bool next() override { + if (m_current >= m_generators.size()) { + return false; + } + const bool current_status = m_generators[m_current].next(); + if (!current_status) { + ++m_current; + } + return m_current < m_generators.size(); + } + }; + + template + GeneratorWrapper> table( std::initializer_list::type...>> tuples ) { + return values>( tuples ); + } + + // Tag type to signal that a generator sequence should convert arguments to a specific type + template + struct as {}; + + template + auto makeGenerators( GeneratorWrapper&& generator, Gs... moreGenerators ) -> Generators { + return Generators(std::move(generator), std::forward(moreGenerators)...); + } + template + auto makeGenerators( GeneratorWrapper&& generator ) -> Generators { + return Generators(std::move(generator)); + } + template + auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators { + return makeGenerators( value( std::forward( val ) ), std::forward( moreGenerators )... ); + } + template + auto makeGenerators( as, U&& val, Gs... moreGenerators ) -> Generators { + return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); + } + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + + template + // Note: The type after -> is weird, because VS2015 cannot parse + // the expression used in the typedef inside, when it is in + // return type. Yeah. + auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval().get()) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); + if (!tracker.hasGenerator()) { + tracker.setGenerator(pf::make_unique>(generatorExpression())); + } + + auto const& generator = static_cast const&>( *tracker.getGenerator() ); + return generator.get(); + } + +} // namespace Generators +} // namespace Catch + +#define GENERATE( ... ) \ + Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) + +// end catch_generators.hpp +// start catch_generators_generic.hpp + +namespace Catch { +namespace Generators { + + template + class TakeGenerator : public IGenerator { + GeneratorWrapper m_generator; + size_t m_returned = 0; + size_t m_target; + public: + TakeGenerator(size_t target, GeneratorWrapper&& generator): + m_generator(std::move(generator)), + m_target(target) + { + assert(target != 0 && "Empty generators are not allowed"); + } + T const& get() const override { + return m_generator.get(); + } + bool next() override { + ++m_returned; + if (m_returned >= m_target) { + return false; + } + + const auto success = m_generator.next(); + // If the underlying generator does not contain enough values + // then we cut short as well + if (!success) { + m_returned = m_target; + } + return success; + } + }; + + template + GeneratorWrapper take(size_t target, GeneratorWrapper&& generator) { + return GeneratorWrapper(pf::make_unique>(target, std::move(generator))); + } + + template + class FilterGenerator : public IGenerator { + GeneratorWrapper m_generator; + Predicate m_predicate; + public: + template + FilterGenerator(P&& pred, GeneratorWrapper&& generator): + m_generator(std::move(generator)), + m_predicate(std::forward

(pred)) + { + if (!m_predicate(m_generator.get())) { + // It might happen that there are no values that pass the + // filter. In that case we throw an exception. + auto has_initial_value = next(); + if (!has_initial_value) { + Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); + } + } + } + + T const& get() const override { + return m_generator.get(); + } + + bool next() override { + bool success = m_generator.next(); + if (!success) { + return false; + } + while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); + return success; + } + }; + + template + GeneratorWrapper filter(Predicate&& pred, GeneratorWrapper&& generator) { + return GeneratorWrapper(std::unique_ptr>(pf::make_unique>(std::forward(pred), std::move(generator)))); + } + + template + class RepeatGenerator : public IGenerator { + GeneratorWrapper m_generator; + mutable std::vector m_returned; + size_t m_target_repeats; + size_t m_current_repeat = 0; + size_t m_repeat_index = 0; + public: + RepeatGenerator(size_t repeats, GeneratorWrapper&& generator): + m_generator(std::move(generator)), + m_target_repeats(repeats) + { + assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); + } + + T const& get() const override { + if (m_current_repeat == 0) { + m_returned.push_back(m_generator.get()); + return m_returned.back(); + } + return m_returned[m_repeat_index]; + } + + bool next() override { + // There are 2 basic cases: + // 1) We are still reading the generator + // 2) We are reading our own cache + + // In the first case, we need to poke the underlying generator. + // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache + if (m_current_repeat == 0) { + const auto success = m_generator.next(); + if (!success) { + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + + // In the second case, we need to move indices forward and check that we haven't run up against the end + ++m_repeat_index; + if (m_repeat_index == m_returned.size()) { + m_repeat_index = 0; + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + }; + + template + GeneratorWrapper repeat(size_t repeats, GeneratorWrapper&& generator) { + return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator))); + } + + template + class MapGenerator : public IGenerator { + // TBD: provide static assert for mapping function, for friendly error message + GeneratorWrapper m_generator; + Func m_function; + // To avoid returning dangling reference, we have to save the values + T m_cache; + public: + template + MapGenerator(F2&& function, GeneratorWrapper&& generator) : + m_generator(std::move(generator)), + m_function(std::forward(function)), + m_cache(m_function(m_generator.get())) + {} + + T const& get() const override { + return m_cache; + } + bool next() override { + const auto success = m_generator.next(); + if (success) { + m_cache = m_function(m_generator.get()); + } + return success; + } + }; + + template + GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { + return GeneratorWrapper( + pf::make_unique>(std::forward(function), std::move(generator)) + ); + } + template + GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { + return GeneratorWrapper( + pf::make_unique>(std::forward(function), std::move(generator)) + ); + } + + template + class ChunkGenerator final : public IGenerator> { + std::vector m_chunk; + size_t m_chunk_size; + GeneratorWrapper m_generator; + bool m_used_up = false; + public: + ChunkGenerator(size_t size, GeneratorWrapper generator) : + m_chunk_size(size), m_generator(std::move(generator)) + { + m_chunk.reserve(m_chunk_size); + m_chunk.push_back(m_generator.get()); + for (size_t i = 1; i < m_chunk_size; ++i) { + if (!m_generator.next()) { + Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk")); + } + m_chunk.push_back(m_generator.get()); + } + } + std::vector const& get() const override { + return m_chunk; + } + bool next() override { + m_chunk.clear(); + for (size_t idx = 0; idx < m_chunk_size; ++idx) { + if (!m_generator.next()) { + return false; + } + m_chunk.push_back(m_generator.get()); + } + return true; + } + }; + + template + GeneratorWrapper> chunk(size_t size, GeneratorWrapper&& generator) { + return GeneratorWrapper>( + pf::make_unique>(size, std::move(generator)) + ); + } + +} // namespace Generators +} // namespace Catch + +// end catch_generators_generic.hpp +// start catch_generators_specific.hpp + +// start catch_context.h + +#include + +namespace Catch { + + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; + + using IConfigPtr = std::shared_ptr; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual IConfigPtr const& getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; + + private: + static IMutableContext *currentContext; + friend IMutableContext& getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); + }; + + inline IMutableContext& getCurrentMutableContext() + { + if( !IMutableContext::currentContext ) + IMutableContext::createContext(); + return *IMutableContext::currentContext; + } + + inline IContext& getCurrentContext() + { + return getCurrentMutableContext(); + } + + void cleanUpContext(); +} + +// end catch_context.h +// start catch_interfaces_config.h + +#include +#include +#include +#include + +namespace Catch { + + enum class Verbosity { + Quiet = 0, + Normal, + High + }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01, + NoTests = 0x02 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; + + class TestSpec; + + struct IConfig : NonCopyable { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual int benchmarkResolutionMultiple() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + }; + + using IConfigPtr = std::shared_ptr; +} + +// end catch_interfaces_config.h +#include + +namespace Catch { +namespace Generators { + +template +class RandomFloatingGenerator final : public IGenerator { + // FIXME: What is the right seed? + std::minstd_rand m_rand; + std::uniform_real_distribution m_dist; + Float m_current_number; +public: + + RandomFloatingGenerator(Float a, Float b): + m_rand(getCurrentContext().getConfig()->rngSeed()), + m_dist(a, b) { + static_cast(next()); + } + + Float const& get() const override { + return m_current_number; + } + bool next() override { + m_current_number = m_dist(m_rand); + return true; + } +}; + +template +class RandomIntegerGenerator final : public IGenerator { + std::minstd_rand m_rand; + std::uniform_int_distribution m_dist; + Integer m_current_number; +public: + + RandomIntegerGenerator(Integer a, Integer b): + m_rand(getCurrentContext().getConfig()->rngSeed()), + m_dist(a, b) { + static_cast(next()); + } + + Integer const& get() const override { + return m_current_number; + } + bool next() override { + m_current_number = m_dist(m_rand); + return true; + } +}; + +// TODO: Ideally this would be also constrained against the various char types, +// but I don't expect users to run into that in practice. +template +typename std::enable_if::value && !std::is_same::value, +GeneratorWrapper>::type +random(T a, T b) { + return GeneratorWrapper( + pf::make_unique>(a, b) + ); +} + +template +typename std::enable_if::value, +GeneratorWrapper>::type +random(T a, T b) { + return GeneratorWrapper( + pf::make_unique>(a, b) + ); +} + +template +class RangeGenerator final : public IGenerator { + T m_current; + T m_end; + T m_step; + bool m_positive; + +public: + RangeGenerator(T const& start, T const& end, T const& step): + m_current(start), + m_end(end), + m_step(step), + m_positive(m_step > T(0)) + { + assert(m_current != m_end && "Range start and end cannot be equal"); + assert(m_step != T(0) && "Step size cannot be zero"); + assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end"); + } + + RangeGenerator(T const& start, T const& end): + RangeGenerator(start, end, (start < end) ? T(1) : T(-1)) + {} + + T const& get() const override { + return m_current; + } + + bool next() override { + m_current += m_step; + return (m_positive) ? (m_current < m_end) : (m_current > m_end); + } +}; + +template +GeneratorWrapper range(T const& start, T const& end, T const& step) { + static_assert(std::is_integral::value && !std::is_same::value, "Type must be an integer"); + return GeneratorWrapper(pf::make_unique>(start, end, step)); +} + +template +GeneratorWrapper range(T const& start, T const& end) { + static_assert(std::is_integral::value && !std::is_same::value, "Type must be an integer"); + return GeneratorWrapper(pf::make_unique>(start, end)); +} + +} // namespace Generators +} // namespace Catch + +// end catch_generators_specific.hpp + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// start catch_test_case_info.h + +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestInvoker; + + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector const& _tags, + SourceLineInfo const& _lineInfo ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::vector tags ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string tagsAsString() const; + + std::string name; + std::string className; + std::string description; + std::vector tags; + std::vector lcaseTags; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase( ITestInvoker* testCase, TestCaseInfo&& info ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + + private: + std::shared_ptr test; + }; + + TestCase makeTestCase( ITestInvoker* testCase, + std::string const& className, + NameAndTags const& nameAndTags, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_case_info.h +// start catch_interfaces_runner.h + +namespace Catch { + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +// end catch_interfaces_runner.h + +#ifdef __OBJC__ +// start catch_objc.hpp + +#import + +#include + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public ITestInvoker { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList( nullptr, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + struct StringHolder : MatcherBase{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + bool match( NSString* arg ) const override { + return false; + } + + NSString* CATCH_ARC_STRONG m_substr; + }; + + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + std::string describe() const override { + return "equals string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + std::string describe() const override { + return "contains string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + std::string describe() const override { + return "starts with: " + Catch::Detail::stringify( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + std::string describe() const override { + return "ends with: " + Catch::Detail::stringify( m_substr ); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix +#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ +{ \ +return @ name; \ +} \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ +{ \ +return @ desc; \ +} \ +-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) + +#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) + +// end catch_objc.hpp +#endif + +#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +// start catch_external_interfaces.h + +// start catch_reporter_bases.hpp + +// start catch_interfaces_reporter.h + +// start catch_config.hpp + +// start catch_test_spec_parser.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_test_spec.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_wildcard_pattern.h + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; + + private: + std::string adjustCase( std::string const& str ) const; + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; +} + +// end catch_wildcard_pattern.h +#include +#include +#include + +namespace Catch { + + class TestSpec { + struct Pattern { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + using PatternPtr = std::shared_ptr; + + class NamePattern : public Pattern { + public: + NamePattern( std::string const& name ); + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ); + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( PatternPtr const& underlyingPattern ); + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + PatternPtr m_underlyingPattern; + }; + + struct Filter { + std::vector m_patterns; + + bool matches( TestCaseInfo const& testCase ) const; + }; + + public: + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; + + private: + std::vector m_filters; + + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec.h +// start catch_interfaces_tag_alias_registry.h + +#include + +namespace Catch { + + struct TagAlias; + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// end catch_interfaces_tag_alias_registry.h +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode = None; + bool m_exclusion = false; + std::size_t m_start = std::string::npos, m_pos = 0; + std::string m_arg; + std::vector m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases = nullptr; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ); + + TestSpecParser& parse( std::string const& arg ); + TestSpec testSpec(); + + private: + void visitChar( char c ); + void startNewMode( Mode mode, std::size_t start ); + void escape(); + std::string subString() const; + + template + void addPattern() { + std::string token = subString(); + for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); + m_escapeChars.clear(); + if( startsWith( token, "exclude:" ) ) { + m_exclusion = true; + token = token.substr( 8 ); + } + if( !token.empty() ) { + TestSpec::PatternPtr pattern = std::make_shared( token ); + if( m_exclusion ) + pattern = std::make_shared( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + + void addFilter(); + }; + TestSpec parseTestSpec( std::string const& arg ); + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec_parser.h +// Libstdc++ doesn't like incomplete classes for unique_ptr + +#include +#include +#include + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct IStream; + + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; + + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + + int abortAfter = -1; + unsigned int rngSeed = 0; + int benchmarkResolutionMultiple = 100; + + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + + std::string outputFilename; + std::string name; + std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER + + std::vector testsOrTags; + std::vector sectionsToRun; + }; + + class Config : public IConfig { + public: + + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; + + std::string const& getFilename() const; + + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; + + std::string getProcessName() const; + std::string const& getReporterName() const; + + std::vector const& getTestsOrTags() const; + std::vector const& getSectionsToRun() const override; + + virtual TestSpec const& testSpec() const override; + bool hasTestFilters() const override; + + bool showHelp() const; + + // IConfig interface + bool allowThrows() const override; + std::ostream& stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; + ShowDurations::OrNot showDurations() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + int benchmarkResolutionMultiple() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + + private: + + IStream const* openStream(); + ConfigData m_data; + + std::unique_ptr m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; + +} // end namespace Catch + +// end catch_config.hpp +// start catch_assertionresult.h + +#include + +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 const& 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; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +// end catch_assertionresult.h +// start catch_option.hpp + +namespace Catch { + + // An optional type + template + class Option { + public: + Option() : nullableValue( nullptr ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = nullptr; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } + + bool operator !() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); + } + + private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; + +} // end namespace Catch + +// end catch_option.hpp +#include +#include +#include +#include +#include + +namespace Catch { + + struct ReporterConfig { + explicit ReporterConfig( IConfigPtr const& _fullConfig ); + + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); + + std::ostream& stream() const; + IConfigPtr fullConfig() const; + + private: + std::ostream* m_stream; + IConfigPtr m_fullConfig; + }; + + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; + }; + + template + struct LazyStat : Option { + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used = false; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ); + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ); + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = delete; + AssertionStats& operator = ( AssertionStats && ) = delete; + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; + virtual ~SectionStats(); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ); + + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); + + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct BenchmarkInfo { + std::string name; + }; + struct BenchmarkStats { + BenchmarkInfo info; + std::size_t iterations; + uint64_t elapsedTimeInNanoseconds; + }; + + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set getSupportedVerbosities() + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + // *** experimental *** + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + // *** experimental *** + virtual void benchmarkEnded( BenchmarkStats const& ) {} + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + // Default empty implementation provided + virtual void fatalErrorEncountered( StringRef name ); + + virtual bool isMulti() const; + }; + using IStreamingReporterPtr = std::unique_ptr; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr; + + struct IReporterRegistry { + using FactoryMap = std::map; + using Listeners = std::vector; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + +} // end namespace Catch + +// end catch_interfaces_reporter.h +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result); + + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); + + template + struct StreamingReporterBase : IStreamingReporter { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const&) override {} + + void testRunStarting(TestRunInfo const& _testRunInfo) override { + currentTestRunInfo = _testRunInfo; + } + void testGroupStarting(GroupInfo const& _groupInfo) override { + currentGroupInfo = _groupInfo; + } + + void testCaseStarting(TestCaseInfo const& _testInfo) override { + currentTestCaseInfo = _testInfo; + } + void sectionStarting(SectionInfo const& _sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); + } + + void sectionEnded(SectionStats const& /* _sectionStats */) override { + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { + currentTestCaseInfo.reset(); + } + void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { + currentGroupInfo.reset(); + } + void testRunEnded(TestRunStats const& /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const&) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + struct CumulativeReporterBase : IStreamingReporter { + template + struct Node { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + using ChildNodes = std::vector>; + T value; + ChildNodes children; + }; + struct SectionNode { + explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} + virtual ~SectionNode() = default; + + bool operator == (SectionNode const& other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == (std::shared_ptr const& other) const { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector>; + using Assertions = std::vector; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() (std::shared_ptr const& node) const { + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } + void operator=(BySectionInfo const&) = delete; + + private: + SectionInfo const& m_other; + }; + + using TestCaseNode = Node; + using TestGroupNode = Node; + using TestRunNode = Node; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + ~CumulativeReporterBase() override = default; + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + void testRunStarting( TestRunInfo const& ) override {} + void testGroupStarting( GroupInfo const& ) override {} + + void testCaseStarting( TestCaseInfo const& ) override {} + + void sectionStarting( SectionInfo const& sectionInfo ) override { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + std::shared_ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = std::make_shared( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = std::make_shared( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = std::move(node); + } + + void assertionStarting(AssertionInfo const&) override {} + + bool assertionEnded(AssertionStats const& assertionStats) override { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast( assertionStats.assertionResult ) ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const& sectionStats) override { + assert(!m_sectionStack.empty()); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& testCaseStats) override { + auto node = std::make_shared(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const& testGroupStats) override { + auto node = std::make_shared(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const&) override {} + + IConfigPtr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ); + + static std::set getSupportedVerbosities(); + + void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const&) override; + }; + +} // end namespace Catch + +// end catch_reporter_bases.hpp +// start catch_console_colour.h + +namespace Catch { + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, + + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour&& other ) noexcept; + Colour& operator=( Colour&& other ) noexcept; + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + bool m_moved = false; + }; + + std::ostream& operator << ( std::ostream& os, Colour const& ); + +} // end namespace Catch + +// end catch_console_colour.h +// start catch_reporter_registrars.hpp + + +namespace Catch { + + template + class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); + } + + virtual std::string getDescription() const override { + return T::getDescription(); + } + }; + + public: + + explicit ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, std::make_shared() ); + } + }; + + template + class ListenerRegistrar { + + class ListenerFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); + } + virtual std::string getDescription() const override { + return std::string(); + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( std::make_shared() ); + } + }; +} + +#if !defined(CATCH_CONFIG_DISABLE) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +#define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#else // CATCH_CONFIG_DISABLE + +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_reporter_registrars.hpp +// Allow users to base their work off existing reporters +// start catch_reporter_compact.h + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + using StreamingReporterBase::StreamingReporterBase; + + ~CompactReporter() override; + + static std::string getDescription(); + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionEnded(SectionStats const& _sectionStats) override; + + void testRunEnded(TestRunStats const& _testRunStats) override; + + }; + +} // end namespace Catch + +// end catch_reporter_compact.h +// start catch_reporter_console.h + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + // Fwd decls + struct SummaryColumn; + class TablePrinter; + + struct ConsoleReporter : StreamingReporterBase { + std::unique_ptr m_tablePrinter; + + ConsoleReporter(ReporterConfig const& config); + ~ConsoleReporter() override; + static std::string getDescription(); + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionStarting(SectionInfo const& _sectionInfo) override; + void sectionEnded(SectionStats const& _sectionStats) override; + + void benchmarkStarting(BenchmarkInfo const& info) override; + void benchmarkEnded(BenchmarkStats const& stats) override; + + void testCaseEnded(TestCaseStats const& _testCaseStats) override; + void testGroupEnded(TestGroupStats const& _testGroupStats) override; + void testRunEnded(TestRunStats const& _testRunStats) override; + + private: + + void lazyPrint(); + + void lazyPrintWithoutClosingBenchmarkTable(); + void lazyPrintRunInfo(); + void lazyPrintGroupInfo(); + void printTestCaseAndSectionHeader(); + + void printClosedHeader(std::string const& _name); + void printOpenHeader(std::string const& _name); + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const& _string, std::size_t indent = 0); + + void printTotals(Totals const& totals); + void printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row); + + void printTotalsDivider(Totals const& totals); + void printSummaryDivider(); + + private: + bool m_headerPrinted = false; + }; + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +// end catch_reporter_console.h +// start catch_reporter_junit.h + +// start catch_xmlwriter.h + +#include + +namespace Catch { + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ); + + ScopedElement( ScopedElement&& other ) noexcept; + ScopedElement& operator=( ScopedElement&& other ) noexcept; + + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, bool indent = true ); + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer = nullptr; + }; + + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name ); + + ScopedElement scopedElement( std::string const& name ); + + XmlWriter& endElement(); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + ReusableStringStream rss; + rss << attribute; + return writeAttribute( name, rss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ); + + XmlWriter& writeComment( std::string const& text ); + + void writeStylesheetRef( std::string const& url ); + + XmlWriter& writeBlankLine(); + + void ensureTagClosed(); + + private: + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +} + +// end catch_xmlwriter.h +namespace Catch { + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter(ReporterConfig const& _config); + + ~JunitReporter() override; + + static std::string getDescription(); + + void noMatchingTestCases(std::string const& /*spec*/) override; + + void testRunStarting(TestRunInfo const& runInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testCaseInfo) override; + bool assertionEnded(AssertionStats const& assertionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEndedCumulative() override; + + void writeGroup(TestGroupNode const& groupNode, double suiteTime); + + void writeTestCase(TestCaseNode const& testCaseNode); + + void writeSection(std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode); + + void writeAssertions(SectionNode const& sectionNode); + void writeAssertion(AssertionStats const& stats); + + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; + }; + +} // end namespace Catch + +// end catch_reporter_junit.h +// start catch_reporter_xml.h + +namespace Catch { + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter(ReporterConfig const& _config); + + ~XmlReporter() override; + + static std::string getDescription(); + + virtual std::string getStylesheetRef() const; + + void writeSourceInfo(SourceLineInfo const& sourceInfo); + + public: // StreamingReporterBase + + void noMatchingTestCases(std::string const& s) override; + + void testRunStarting(TestRunInfo const& testInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testInfo) override; + + void sectionStarting(SectionInfo const& sectionInfo) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& assertionStats) override; + + void sectionEnded(SectionStats const& sectionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEnded(TestRunStats const& testRunStats) override; + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; + +} // end namespace Catch + +// end catch_reporter_xml.h + +// end catch_external_interfaces.h +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +#ifdef CATCH_IMPL +// start catch_impl.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// Keep these here for external reporters +// start catch_test_case_tracker.h + +#include +#include +#include + +namespace Catch { +namespace TestCaseTracking { + + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + }; + + struct ITracker; + + using ITrackerPtr = std::shared_ptr; + + struct ITracker { + virtual ~ITracker(); + + // static queries + virtual NameAndLocation const& nameAndLocation() const = 0; + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild( ITrackerPtr const& child ) = 0; + virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isGeneratorTracker() const = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + ITrackerPtr m_rootTracker; + ITracker* m_currentTracker = nullptr; + RunState m_runState = NotStarted; + + public: + + static TrackerContext& instance(); + + ITracker& startRun(); + void endRun(); + + void startCycle(); + void completeCycle(); + + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + + using Children = std::vector; + NameAndLocation m_nameAndLocation; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState = NotStarted; + + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + NameAndLocation const& nameAndLocation() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; + + void addChild( ITrackerPtr const& child ) override; + + ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + ITracker& parent() override; + + void openChild() override; + + bool isSectionTracker() const override; + bool isGeneratorTracker() const override; + + void open(); + + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; + + private: + void moveToParent(); + void moveToThis(); + }; + + class SectionTracker : public TrackerBase { + std::vector m_filters; + public: + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + bool isSectionTracker() const override; + + bool isComplete() const override; + + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); + + void tryOpen(); + + void addInitialFilters( std::vector const& filters ); + void addNextFilters( std::vector const& filters ); + }; + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // namespace Catch + +// end catch_test_case_tracker.h + +// start catch_leak_detector.h + +namespace Catch { + + struct LeakDetector { + LeakDetector(); + ~LeakDetector(); + }; + +} +// end catch_leak_detector.h +// Cpp files will be included in the single-header file here +// start catch_approx.cpp + +#include +#include + +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 { +namespace Detail { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits::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(m_value))); + } + + void Approx::setMargin(double margin) { + CATCH_ENFORCE(margin >= 0, + "Invalid Approx::margin: " << margin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = margin; + } + + void Approx::setEpsilon(double epsilon) { + CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0, + "Invalid Approx::epsilon: " << epsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = epsilon; + } + +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val) { + return Detail::Approx(val); + } + Detail::Approx operator "" _a(unsigned long long val) { + return Detail::Approx(val); + } +} // end namespace literals + +std::string StringMaker::convert(Catch::Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch +// end catch_approx.cpp +// start catch_assertionhandler.cpp + +// start catch_debugger.h + +namespace Catch { + bool isDebuggerActive(); +} + +#ifdef CATCH_PLATFORM_MAC + + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif +#elif defined(_MSC_VER) + #define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_TRAP() DebugBreak() +#endif + +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }() +#else + #define CATCH_BREAK_INTO_DEBUGGER() []{}() +#endif + +// end catch_debugger.h +// start catch_run_context.h + +// start catch_fatal_condition.h + +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX +# define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + + struct FatalConditionHandler { + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + FatalConditionHandler(); + static void reset(); + ~FatalConditionHandler(); + + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + +} // namespace Catch + +#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) + +#include + +namespace Catch { + + struct FatalConditionHandler { + + static bool isSet; + static struct sigaction oldSigActions[]; + static stack_t oldSigStack; + static char altStackMem[]; + + static void handleSignal( int sig ); + + FatalConditionHandler(); + ~FatalConditionHandler(); + static void reset(); + }; + +} // namespace Catch + +#else + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +#endif + +// end catch_fatal_condition.h +#include + +namespace Catch { + + struct IMutableContext; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + public: + RunContext( RunContext const& ) = delete; + RunContext& operator =( RunContext const& ) = delete; + + explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); + + ~RunContext() override; + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); + + Totals runTest(TestCase const& testCase); + + IConfigPtr config() const; + IStreamingReporter& reporter() const; + + public: // IResultCapture + + // Assertion handlers + void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) override; + void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) override; + void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) override; + void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) override; + void handleIncomplete + ( AssertionInfo const& info ) override; + void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) override; + + bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; + + void sectionEnded( SectionEndInfo const& endInfo ) override; + void sectionEndedEarly( SectionEndInfo const& endInfo ) override; + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; + + void benchmarkStarting( BenchmarkInfo const& info ) override; + void benchmarkEnded( BenchmarkStats const& stats ) override; + + void pushScopedMessage( MessageInfo const& message ) override; + void popScopedMessage( MessageInfo const& message ) override; + + void emplaceUnscopedMessage( MessageBuilder const& builder ) override; + + std::string getCurrentTestName() const override; + + const AssertionResult* getLastResult() const override; + + void exceptionEarlyReported() override; + + void handleFatalErrorCondition( StringRef message ) override; + + bool lastAssertionPassed() override; + + void assertionPassed() override; + + public: + // !TBD We need to do this another way! + bool aborting() const final; + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); + void invokeActiveTestCase(); + + void resetAssertionInfo(); + bool testForMissingAssertions( Counts& assertions ); + + void assertionEnded( AssertionResult const& result ); + void reportExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ); + + void populateReaction( AssertionReaction& reaction ); + + private: + + void handleUnfinishedSections(); + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase = nullptr; + ITracker* m_testCaseTracker = nullptr; + Option m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector m_messages; + std::vector m_messageScopes; /* Keeps owners of so-called unscoped messages. */ + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; + }; + +} // end namespace Catch + +// end catch_run_context.h +namespace Catch { + + namespace { + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; + } + } + + LazyExpression::LazyExpression( bool isNegated ) + : m_isNegated( isNegated ) + {} + + LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} + + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } + + auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { + if( lazyExpr.m_isNegated ) + os << "!"; + + if( lazyExpr ) { + if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } + else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } + + AssertionHandler::AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, + m_resultCapture( getResultCapture() ) + {} + + void AssertionHandler::handleExpr( ITransientExpression const& expr ) { + m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); + } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + } + + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } + + void AssertionHandler::complete() { + setCompleted(); + if( m_reaction.shouldDebugBreak ) { + + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); + } + if (m_reaction.shouldThrow) { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw Catch::TestFailureException(); +#else + CATCH_ERROR( "Test failure requires aborting test!" ); +#endif + } + } + void AssertionHandler::setCompleted() { + m_completed = true; + } + + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); + } + + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); + } + + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + } + +} // namespace Catch +// end catch_assertionhandler.cpp +// start catch_assertionresult.cpp + +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 const& data ) + : m_info( info ), + m_resultData( 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[0] != 0; + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!(" + m_info.capturedExpression + ")"; + else + return m_info.capturedExpression; + } + + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName[0] == 0 ) + expr = m_info.capturedExpression; + else { + 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; + } + + std::string 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 +// end catch_assertionresult.cpp +// start catch_benchmark.cpp + +namespace Catch { + + auto BenchmarkLooper::getResolution() -> uint64_t { + return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); + } + + void BenchmarkLooper::reportStart() { + getResultCapture().benchmarkStarting( { m_name } ); + } + auto BenchmarkLooper::needsMoreIterations() -> bool { + auto elapsed = m_timer.getElapsedNanoseconds(); + + // Exponentially increasing iterations until we're confident in our timer resolution + if( elapsed < m_resolution ) { + m_iterationsToRun *= 10; + return true; + } + + getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); + return false; + } + +} // end namespace Catch +// end catch_benchmark.cpp +// start catch_capture_matchers.cpp + +namespace Catch { + + using StringMatcher = Matchers::Impl::MatcherBase; + + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr( exceptionMessage, matcher, matcherString ); + handler.handleExpr( expr ); + } + +} // namespace Catch +// end catch_capture_matchers.cpp +// start catch_commandline.cpp + +// start catch_commandline.h + +// start catch_clara.h + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#endif +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// start clara.hpp +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See https://github.com/philsquared/Clara for more details + +// Clara v1.1.5 + + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#include +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// This project is hosted at https://github.com/philsquared/textflowcpp + + +#include +#include +#include +#include + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { +namespace clara { +namespace TextFlow { + +inline auto isWhitespace(char c) -> bool { + static std::string chars = " \t\n\r"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableBefore(char c) -> bool { + static std::string chars = "[({<|"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableAfter(char c) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find(c) != std::string::npos; +} + +class Columns; + +class Column { + std::vector m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + +public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator(Column const& column, size_t stringIndex) + : m_column(column), + m_stringIndex(stringIndex) {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary(size_t at) const -> bool { + assert(at > 0); + assert(at <= line().size()); + + return at == line().size() || + (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || + isBreakableBefore(line()[at]) || + isBreakableAfter(line()[at - 1]); + } + + void calcLength() { + assert(m_stringIndex < m_column.m_strings.size()); + + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + while (m_end < line().size() && line()[m_end] != '\n') + ++m_end; + + if (m_end < m_pos + width) { + m_len = m_end - m_pos; + } else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace(line()[m_pos + len - 1])) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Column const& column) : m_column(column) { + assert(m_column.m_width > m_column.m_indent); + assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_len == 0) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if (m_pos < line().size() && line()[m_pos] == '\n') + m_pos += 1; + else + while (m_pos < line().size() && isWhitespace(line()[m_pos])) + ++m_pos; + + if (m_pos == line().size()) { + m_pos = 0; + ++m_stringIndex; + } + if (m_stringIndex < m_column.m_strings.size()) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + + auto operator ==(iterator const& other) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=(iterator const& other) const -> bool { + return !operator==(other); + } + }; + using const_iterator = iterator; + + explicit Column(std::string const& text) { m_strings.push_back(text); } + + auto width(size_t newWidth) -> Column& { + assert(newWidth > 0); + m_width = newWidth; + return *this; + } + auto indent(size_t newIndent) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent(size_t newIndent) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << (std::ostream& os, Column const& col) { + bool first = true; + for (auto line : col) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + (Column const& other)->Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +class Spacer : public Column { + +public: + explicit Spacer(size_t spaceWidth) : Column("") { + width(spaceWidth); + } +}; + +class Columns { + std::vector m_columns; + +public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator(Columns const& columns, EndTag) + : m_columns(columns.m_columns), + m_activeIterators(0) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) + m_iterators.push_back(col.end()); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Columns const& columns) + : m_columns(columns.m_columns), + m_activeIterators(m_columns.size()) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) + m_iterators.push_back(col.begin()); + } + + auto operator ==(iterator const& other) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=(iterator const& other) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + padding = std::string(width - col.size(), ' '); + else + padding = ""; + } else { + padding += std::string(width, ' '); + } + } + return row; + } + auto operator ++() -> iterator& { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += (Column const& col) -> Columns& { + m_columns.push_back(col); + return *this; + } + auto operator + (Column const& col) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) { + + bool first = true; + for (auto line : cols) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +inline auto Column::operator + (Column const& other) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; +} +} + +} +} + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + +#include +#include +#include +#include +#include + +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS +#endif + +namespace Catch { namespace clara { +namespace detail { + + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args( int argc, char const* const* argv ) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} + + Args( std::initializer_list args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); + } + } + } + + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); + } + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; + + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; + + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() override { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + void enforceOk() const override { + + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert( m_type != ResultBase::LogicError ); + assert( m_type != ResultBase::RuntimeError ); + if( m_type != ResultBase::Ok ) + std::abort(); + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( std::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + } +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template + inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { + T temp; + auto result = convertInto( source, temp ); + if( result ) + target = std::move(temp); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + + struct NonCopyable { + NonCopyable() = default; + NonCopyable( NonCopyable const & ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable &operator=( NonCopyable const & ) = delete; + NonCopyable &operator=( NonCopyable && ) = delete; + }; + + struct BoundRef : NonCopyable { + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag( bool flag ) -> ParserResult = 0; + virtual auto isFlag() const -> bool { return true; } + }; + + template + struct BoundValueRef : BoundValueRefBase { + T &m_ref; + + explicit BoundValueRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template + struct BoundValueRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + struct LambdaInvoker { + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp{}; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + } + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const &other ) const -> Parser; + + template + auto operator+( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + + public: + template + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} + + template + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared( "" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared>( ref ); + } + + template + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + assert( !m_ref->isFlag() ); + auto valueRef = static_cast( m_ref.get() ); + + auto result = valueRef->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CATCH_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + + class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + + template + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto flagRef = static_cast( m_ref.get() ); + auto result = flagRef->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + auto valueRef = static_cast( m_ref.get() ); + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = valueRef->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CATCH_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template + auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } + template + auto operator+( T const &other ) const -> Parser { return operator|( other ); } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + optWidth = (std::min)(optWidth, consoleWidth/2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; + } + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template + template + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; + } +} // namespace detail + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + +}} // namespace Catch::clara + +// end clara.hpp +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// end catch_clara.h +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ); + +} // end namespace Catch + +// end catch_commandline.h +#include +#include + +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ) { + + using namespace clara; + + auto const setWarning = [&]( std::string const& warning ) { + auto warningSet = [&]() { + if( warning == "NoAssertions" ) + return WarnAbout::NoAssertions; + + if ( warning == "NoTests" ) + return WarnAbout::NoTests; + + return WarnAbout::Nothing; + }(); + + if (warningSet == WarnAbout::Nothing) + return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); + config.warnings = static_cast( config.warnings | warningSet ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + config.testsOrTags.push_back( line + ',' ); + } + } + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed != "time" ) + return clara::detail::convertInto( seed, config.rngSeed ); + config.rngSeed = static_cast( std::time(nullptr) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setColourUsage = [&]( std::string const& useColour ) { + auto mode = toLower( useColour ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setVerbosity = [&]( std::string const& verbosity ) { + auto lcVerbosity = toLower( verbosity ); + if( lcVerbosity == "quiet" ) + config.verbosity = Verbosity::Quiet; + else if( lcVerbosity == "normal" ) + config.verbosity = Verbosity::Normal; + else if( lcVerbosity == "high" ) + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setReporter = [&]( std::string const& reporter ) { + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + + auto lcReporter = toLower( reporter ); + auto result = factories.find( lcReporter ); + + if( factories.end() != result ) + config.reporterName = lcReporter; + else + return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.listTests ) + ["-l"]["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["-t"]["--list-tags"] + ( "list all/matching tags" ) + | Opt( config.showSuccessfulTests ) + ["-s"]["--success"] + ( "include successful tests in output" ) + | Opt( config.shouldDebugBreak ) + ["-b"]["--break"] + ( "break into debugger on failure" ) + | Opt( config.noThrow ) + ["-e"]["--nothrow"] + ( "skip exception tests" ) + | Opt( config.showInvisibles ) + ["-i"]["--invisibles"] + ( "show invisibles (tabs, newlines)" ) + | Opt( config.outputFilename, "filename" ) + ["-o"]["--out"] + ( "output filename" ) + | Opt( setReporter, "name" ) + ["-r"]["--reporter"] + ( "reporter to use (defaults to console)" ) + | Opt( config.name, "name" ) + ["-n"]["--name"] + ( "suite name" ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) + ["-a"]["--abort"] + ( "abort at first failure" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + ["-x"]["--abortx"] + ( "abort after x failures" ) + | Opt( setWarning, "warning name" ) + ["-w"]["--warn"] + ( "enable warnings" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) + | Opt( config.filenamesAsTags ) + ["-#"]["--filenames-as-tags"] + ( "adds a tag for the filename" ) + | Opt( config.sectionsToRun, "section name" ) + ["-c"]["--section"] + ( "specify section to run" ) + | Opt( setVerbosity, "quiet|normal|high" ) + ["-v"]["--verbosity"] + ( "set output verbosity" ) + | Opt( config.listTestNamesOnly ) + ["--list-test-names-only"] + ( "list all/matching test cases names only" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all reporters" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setColourUsage, "yes|no" ) + ["--use-colour"] + ( "should output be colourised" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.benchmarkResolutionMultiple, "multiplier" ) + ["--benchmark-resolution-multiple"] + ( "multiple of clock resolution to run benchmarks" ) + + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); + + return cli; + } + +} // end namespace Catch +// end catch_commandline.cpp +// start catch_common.cpp + +#include +#include + +namespace Catch { + + bool SourceLineInfo::empty() const noexcept { + return file[0] == '\0'; + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; + } + + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; + +} +// end catch_common.cpp +// start catch_config.cpp + +namespace Catch { + + Config::Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + TestSpecParser parser(ITagAliasRegistry::get()); + if (data.testsOrTags.empty()) { + parser.parse("~[.]"); // All not hidden tests + } + else { + m_hasTestFilters = true; + for( auto const& testOrTags : data.testsOrTags ) + parser.parse( testOrTags ); + } + m_testSpec = parser.testSpec(); + } + + std::string const& Config::getFilename() const { + return m_data.outputFilename ; + } + + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } + + std::string Config::getProcessName() const { return m_data.processName; } + std::string const& Config::getReporterName() const { return m_data.reporterName; } + + std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } + std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + + 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; } + std::ostream& Config::stream() const { return m_stream->stream(); } + std::string 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::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + 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; } + + IStream const* Config::openStream() { + return Catch::makeStream(m_data.outputFilename); + } + +} // end namespace Catch +// end catch_config.cpp +// start catch_console_colour.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +// start catch_errno_guard.h + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard(); + ~ErrnoGuard(); + private: + int m_oldErrno; + }; + +} + +// end catch_errno_guard.h +#include + +namespace Catch { + namespace { + + struct IColourImpl { + virtual ~IColourImpl() = default; + virtual void use( Colour::Code _colourCode ) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // anon namespace +} // namespace Catch + +#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif +#endif + +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// + +namespace Catch { +namespace { + + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + virtual void use( Colour::Code _colourCode ) override { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + + default: + CATCH_ERROR( "Unknown colour requested" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = UseColour::Yes; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// + +#include + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) override { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0;34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + case Colour::BrightYellow: return setColour( "[1;33m" ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour( const char* _escapeCode ) { + getCurrentContext().getConfig()->stream() + << '\033' << _escapeCode; + } + }; + + bool useColourOnPlatform() { + return +#ifdef CATCH_PLATFORM_MAC + !isDebuggerActive() && +#endif +#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) + isatty(STDOUT_FILENO) +#else + false +#endif + ; + } + IColourImpl* platformColourInstance() { + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = useColourOnPlatform() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + + static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + + Colour::Colour( Code _colourCode ) { use( _colourCode ); } + Colour::Colour( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + } + Colour& Colour::operator=( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + return *this; + } + + Colour::~Colour(){ if( !m_moved ) use( None ); } + + void Colour::use( Code _colourCode ) { + static IColourImpl* impl = platformColourInstance(); + impl->use( _colourCode ); + } + + std::ostream& operator << ( std::ostream& os, Colour const& ) { + return os; + } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_console_colour.cpp +// start catch_context.cpp + +namespace Catch { + + class Context : public IMutableContext, NonCopyable { + + public: // IContext + virtual IResultCapture* getResultCapture() override { + return m_resultCapture; + } + virtual IRunner* getRunner() override { + return m_runner; + } + + virtual IConfigPtr const& getConfig() const override { + return m_config; + } + + virtual ~Context() override; + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) override { + m_runner = runner; + } + virtual void setConfig( IConfigPtr const& config ) override { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner* m_runner = nullptr; + IResultCapture* m_resultCapture = nullptr; + }; + + IMutableContext *IMutableContext::currentContext = nullptr; + + void IMutableContext::createContext() + { + currentContext = new Context(); + } + + void cleanUpContext() { + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; + } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; +} +// end catch_context.cpp +// start catch_debug_console.cpp + +// start catch_debug_console.h + +#include + +namespace Catch { + void writeToDebugConsole( std::string const& text ); +} + +// end catch_debug_console.h +#ifdef CATCH_PLATFORM_WINDOWS + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } + +#else + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } + +#endif // Platform +// end catch_debug_console.cpp +// start catch_debugger.cpp + +#ifdef CATCH_PLATFORM_MAC + +# include +# include +# include +# include +# include +# include +# include + +namespace Catch { + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + std::size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + bool isDebuggerActive() { return false; } + } +#endif // Platform +// end catch_debugger.cpp +// start catch_decomposer.cpp + +namespace Catch { + + ITransientExpression::~ITransientExpression() = default; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; + } +} +// end catch_decomposer.cpp +// start catch_enforce.cpp + +namespace Catch { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) + [[noreturn]] + void throw_exception(std::exception const& e) { + Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); + } +#endif +} // namespace Catch; +// end catch_enforce.cpp +// start catch_errno_guard.cpp + +#include + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} +// end catch_errno_guard.cpp +// start catch_exception_translator_registry.cpp + +// start catch_exception_translator_registry.h + +#include +#include +#include + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator( const IExceptionTranslator* translator ); + virtual std::string translateActiveException() const override; + std::string tryTranslators() const; + + private: + std::vector> m_translators; + }; +} + +// end catch_exception_translator_registry.h +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } + + void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( std::unique_ptr( translator ) ); + } + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::Detail::stringify( [exception description] ); + } +#else + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if (std::current_exception() == nullptr) { + return "Non C++ exception. Possibly a CLR exception."; + } + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if (m_translators.empty()) { + std::rethrow_exception(std::current_exception()); + } else { + return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); + } + } + +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + std::string ExceptionTranslatorRegistry::translateActiveException() const { + CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } +#endif + +} +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace { + // Report the error condition + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } +} + +#endif // signals/SEH handling + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = nullptr; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + void FatalConditionHandler::reset() { + if (isSet) { + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = nullptr; + isSet = false; + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + +bool FatalConditionHandler::isSet = false; +ULONG FatalConditionHandler::guaranteeSize = 0; +PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; + +} // namespace Catch + +#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + + // 32kb for the alternate stack seems to be sufficient. However, this value + // is experimentally determined, so that's not guaranteed. + constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; + + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + void FatalConditionHandler::handleSignal( int sig ) { + char const * name = ""; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = sigStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + + void FatalConditionHandler::reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; + } + } + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[sigStackSize] = {}; + +} // namespace Catch + +#else + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +#endif // signals/SEH handling + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +// end catch_fatal_condition.cpp +// start catch_generators.cpp + +// start catch_random_number_generator.h + +#include +#include + +namespace Catch { + + struct IConfig; + + std::mt19937& rng(); + void seedRng( IConfig const& config ); + unsigned int rngSeed(); + +} + +// end catch_random_number_generator.h +#include +#include + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() {} + +const char* GeneratorException::what() const noexcept { + return m_msg; +} + +namespace Generators { + + GeneratorUntypedBase::~GeneratorUntypedBase() {} + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( lineInfo ); + } + +} // namespace Generators +} // namespace Catch +// end catch_generators.cpp +// start catch_interfaces_capture.cpp + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} +// end catch_interfaces_capture.cpp +// start catch_interfaces_config.cpp + +namespace Catch { + IConfig::~IConfig() = default; +} +// end catch_interfaces_config.cpp +// start catch_interfaces_exception.cpp + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} +// end catch_interfaces_exception.cpp +// start catch_interfaces_registry_hub.cpp + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} +// end catch_interfaces_registry_hub.cpp +// start catch_interfaces_reporter.cpp + +// start catch_reporter_listening.h + +namespace Catch { + + class ListeningReporter : public IStreamingReporter { + using Reporters = std::vector; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; + + public: + ListeningReporter(); + + void addListener( IStreamingReporterPtr&& listener ); + void addReporter( IStreamingReporterPtr&& reporter ); + + public: // IStreamingReporter + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases( std::string const& spec ) override; + + static std::set getSupportedVerbosities(); + + void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; + void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; + + void testRunStarting( TestRunInfo const& testRunInfo ) override; + void testGroupStarting( GroupInfo const& groupInfo ) override; + void testCaseStarting( TestCaseInfo const& testInfo ) override; + void sectionStarting( SectionInfo const& sectionInfo ) override; + void assertionStarting( AssertionInfo const& assertionInfo ) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded( AssertionStats const& assertionStats ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + void testCaseEnded( TestCaseStats const& testCaseStats ) override; + void testGroupEnded( TestGroupStats const& testGroupStats ) override; + void testRunEnded( TestRunStats const& testRunStats ) override; + + void skipTest( TestCaseInfo const& testInfo ) override; + bool isMulti() const override; + + }; + +} // end namespace Catch + +// end catch_reporter_listening.h +namespace Catch { + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& ReporterConfig::stream() const { return *m_stream; } + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + + TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + + GroupInfo::GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; + + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + SectionStats::~SectionStats() = default; + + TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + + TestCaseStats::~TestCaseStats() = default; + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestRunStats::~TestRunStats() = default; + + void IStreamingReporter::fatalErrorEncountered( StringRef ) {} + bool IStreamingReporter::isMulti() const { return false; } + + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; + +} // end namespace Catch +// end catch_interfaces_reporter.cpp +// start catch_interfaces_runner.cpp + +namespace Catch { + IRunner::~IRunner() = default; +} +// end catch_interfaces_runner.cpp +// start catch_interfaces_testcase.cpp + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} +// end catch_interfaces_testcase.cpp +// start catch_leak_detector.cpp + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include + +namespace Catch { + + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} + +#else + + Catch::LeakDetector::LeakDetector() {} + +#endif + +Catch::LeakDetector::~LeakDetector() { + Catch::cleanUp(); +} +// end catch_leak_detector.cpp +// start catch_list.cpp + +// start catch_list.h + +#include + +namespace Catch { + + std::size_t listTests( Config const& config ); + + std::size_t listTestsNamesOnly( Config const& config ); + + struct TagInfo { + void add( std::string const& spelling ); + std::string all() const; + + std::set spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters(); + + Option list( std::shared_ptr const& config ); + +} // end namespace Catch + +// end catch_list.h +// start catch_text.h + +namespace Catch { + using namespace clara::TextFlow; +} + +// end catch_text.h +#include +#include +#include + +namespace Catch { + + std::size_t listTests( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + } + + auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; + if( config.verbosity() >= Verbosity::High ) { + Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column( description ).indent(4) << std::endl; + } + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; + } + + if( !config.hasTestFilters() ) + Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + matchedTests++; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.verbosity() >= Verbosity::High ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } + + void TagInfo::add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + std::string out; + for( auto const& spelling : spellings ) + out += "[" + spelling + "]"; + return out; + } + + std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCase : matchedTestCases ) { + for( auto const& tagName : testCase.getTestCaseInfo().tags ) { + std::string lcaseTagName = toLower( tagName ); + auto countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( auto const& tagCount : tagCounts ) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column( tagCount.second.all() ) + .initialIndent( 0 ) + .indent( str.size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); + } + + std::size_t listReporters() { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for( auto const& factoryKvp : factories ) + maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + + for( auto const& factoryKvp : factories ) { + Catch::cout() + << Column( factoryKvp.first + ":" ) + .indent(2) + .width( 5+maxNameLen ) + + Column( factoryKvp.second->getDescription() ) + .initialIndent(0) + .indent(2) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + Option list( std::shared_ptr const& config ) { + Option listedCount; + getCurrentMutableContext().setConfig( config ); + if( config->listTests() ) + listedCount = listedCount.valueOr(0) + listTests( *config ); + if( config->listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config ); + if( config->listTags() ) + listedCount = listedCount.valueOr(0) + listTags( *config ); + if( config->listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters(); + return listedCount; + } + +} // end namespace Catch +// end catch_list.cpp +// start catch_matchers.cpp + +namespace Catch { +namespace Matchers { + namespace Impl { + + std::string MatcherUntypedBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + + } // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch +// end catch_matchers.cpp +// start catch_matchers_floating.cpp + +// start catch_polyfills.hpp + +namespace Catch { + bool isnan(float f); + bool isnan(double d); +} + +// end catch_polyfills.hpp +// start catch_to_string.hpp + +#include + +namespace Catch { + template + std::string to_string(T const& t) { +#if defined(CATCH_CONFIG_CPP11_TO_STRING) + return std::to_string(t); +#else + ReusableStringStream rss; + rss << t; + return rss.str(); +#endif + } +} // end namespace Catch + +// end catch_to_string.hpp +#include +#include +#include + +namespace Catch { +namespace Matchers { +namespace Floating { +enum class FloatingPointKind : uint8_t { + Float, + Double +}; +} +} +} + +namespace { + +template +struct Converter; + +template <> +struct Converter { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + Converter(float f) { + std::memcpy(&i, &f, sizeof(f)); + } + int32_t i; +}; + +template <> +struct Converter { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + Converter(double d) { + std::memcpy(&i, &d, sizeof(d)); + } + int64_t i; +}; + +template +auto convert(T t) -> Converter { + return Converter(t); +} + +template +bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (Catch::isnan(lhs) || Catch::isnan(rhs)) { + return false; + } + + auto lc = convert(lhs); + auto rc = convert(rhs); + + if ((lc.i < 0) != (rc.i < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } + + auto ulpDiff = std::abs(lc.i - rc.i); + return ulpDiff <= maxUlpDiff; +} + +} + +namespace Catch { +namespace Matchers { +namespace Floating { + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + :m_target{ target }, m_margin{ margin } { + CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' + << " Margin has to be non-negative."); + } + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const& matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); + } + + std::string WithinAbsMatcher::describe() const { + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); + } + + WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) + :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { + CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.' + << " ULPs have to be non-negative."); + } + +#if defined(__clang__) +#pragma clang diagnostic push +// Clang <3.5 reports on the default branch in the switch below +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + bool WithinUlpsMatcher::match(double const& matchee) const { + switch (m_type) { + case FloatingPointKind::Float: + return almostEqualUlps(static_cast(matchee), static_cast(m_target), m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps(matchee, m_target, m_ulps); + default: + CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" ); + } + } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + std::string WithinUlpsMatcher::describe() const { + return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); + } + +}// namespace Floating + +Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); +} + +Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.cpp +// start catch_matchers_generic.cpp + +std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} +// end catch_matchers_generic.cpp +// start catch_matchers_string.cpp + +#include + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} + + bool RegexMatcher::match(std::string const& matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::Choice::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); + } + + std::string RegexMatcher::describe() const { + return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively"); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + + StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) { + return StdString::RegexMatcher(regex, caseSensitivity); + } + +} // namespace Matchers +} // namespace Catch +// end catch_matchers_string.cpp +// start catch_message.cpp + +// start catch_uncaught_exceptions.h + +namespace Catch { + bool uncaught_exceptions(); +} // end namespace Catch + +// end catch_uncaught_exceptions.h +#include +#include + +namespace Catch { + + MessageInfo::MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + bool MessageInfo::operator==( MessageInfo const& other ) const { + return sequence == other.sequence; + } + + bool MessageInfo::operator<( MessageInfo const& other ) const { + return sequence < other.sequence; + } + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + Catch::MessageBuilder::MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + :m_info(macroName, lineInfo, type) {} + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ), m_moved() + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + + ScopedMessage::ScopedMessage( ScopedMessage&& old ) + : m_info( old.m_info ), m_moved() + { + 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 ) { + auto trimmed = [&] (size_t start, size_t end) { + while (names[start] == ',' || isspace(names[start])) { + ++start; + } + while (names[end] == ',' || isspace(names[end])) { + --end; + } + return names.substr(start, end - start + 1); + }; + + size_t start = 0; + std::stack 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 ',': + if (start != pos && openings.size() == 0) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = trimmed(start, pos); + m_messages.back().message += " := "; + start = pos; + } + } + } + assert(openings.size() == 0 && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = 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 +// end catch_message.cpp +// start catch_output_redirect.cpp + +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H + +#include +#include +#include + +namespace Catch { + + class RedirectedStream { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; + + public: + RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); + ~RedirectedStream(); + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut(); + auto str() const -> std::string; + }; + + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr(); + auto str() const -> std::string; + }; + + class RedirectedStreams { + public: + RedirectedStreams(RedirectedStreams const&) = delete; + RedirectedStreams& operator=(RedirectedStreams const&) = delete; + RedirectedStreams(RedirectedStreams&&) = delete; + RedirectedStreams& operator=(RedirectedStreams&&) = delete; + + RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr); + ~RedirectedStreams(); + private: + std::string& m_redirectedCout; + std::string& m_redirectedCerr; + RedirectedStdOut m_redirectedStdOut; + RedirectedStdErr m_redirectedStdErr; + }; + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile(TempFile const&) = delete; + TempFile& operator=(TempFile const&) = delete; + TempFile(TempFile&&) = delete; + TempFile& operator=(TempFile&&) = delete; + + TempFile(); + ~TempFile(); + + std::FILE* getFile(); + std::string getContents(); + + private: + std::FILE* m_file = nullptr; + #if defined(_MSC_VER) + char m_buffer[L_tmpnam] = { 0 }; + #endif + }; + + class OutputRedirect { + public: + OutputRedirect(OutputRedirect const&) = delete; + OutputRedirect& operator=(OutputRedirect const&) = delete; + OutputRedirect(OutputRedirect&&) = delete; + OutputRedirect& operator=(OutputRedirect&&) = delete; + + OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); + ~OutputRedirect(); + + private: + int m_originalStdout = -1; + int m_originalStderr = -1; + TempFile m_stdoutFile; + TempFile m_stderrFile; + std::string& m_stdoutDest; + std::string& m_stderrDest; + }; + +#endif + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +// end catch_output_redirect.h +#include +#include +#include +#include +#include + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #include //_dup and _dup2 + #define dup _dup + #define dup2 _dup2 + #define fileno _fileno + #else + #include // dup and dup2 + #endif +#endif + +namespace Catch { + + RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) + { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + + RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + + RedirectedStdErr::RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + + RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) + : m_redirectedCout(redirectedCout), + m_redirectedCerr(redirectedCerr) + {} + + RedirectedStreams::~RedirectedStreams() { + m_redirectedCout += m_redirectedStdOut.str(); + m_redirectedCerr += m_redirectedStdErr.str(); + } + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +#if defined(_MSC_VER) + TempFile::TempFile() { + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); + } + if (fopen_s(&m_file, m_buffer, "w")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR("Could not translate errno to a string"); + } + CATCH_RUNTIME_ERROR("Coul dnot open the temp file: '" << m_buffer << "' because: " << buffer); + } + } +#else + TempFile::TempFile() { + m_file = std::tmpfile(); + if (!m_file) { + CATCH_RUNTIME_ERROR("Could not create a temp file."); + } + } + +#endif + + TempFile::~TempFile() { + // TBD: What to do about errors here? + std::fclose(m_file); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +#if defined(_MSC_VER) + std::remove(m_buffer); +#endif + } + + FILE* TempFile::getFile() { + return m_file; + } + + std::string TempFile::getContents() { + std::stringstream sstr; + char buffer[100] = {}; + std::rewind(m_file); + while (std::fgets(buffer, sizeof(buffer), m_file)) { + sstr << buffer; + } + return sstr.str(); + } + + OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : + m_originalStdout(dup(1)), + m_originalStderr(dup(2)), + m_stdoutDest(stdout_dest), + m_stderrDest(stderr_dest) { + dup2(fileno(m_stdoutFile.getFile()), 1); + dup2(fileno(m_stderrFile.getFile()), 2); + } + + OutputRedirect::~OutputRedirect() { + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + + dup2(m_originalStdout, 1); + dup2(m_originalStderr, 2); + + m_stdoutDest += m_stdoutFile.getContents(); + m_stderrDest += m_stderrFile.getContents(); + } + +#endif // CATCH_CONFIG_NEW_CAPTURE + +} // namespace Catch + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #undef dup + #undef dup2 + #undef fileno + #endif +#endif +// end catch_output_redirect.cpp +// start catch_polyfills.cpp + +#include + +namespace Catch { + +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) + bool isnan(float f) { + return std::isnan(f); + } + bool isnan(double d) { + return std::isnan(d); + } +#else + // For now we only use this for embarcadero + bool isnan(float f) { + return std::_isnan(f); + } + bool isnan(double d) { + return std::_isnan(d); + } +#endif + +} // end namespace Catch +// end catch_polyfills.cpp +// start catch_random_number_generator.cpp + +namespace Catch { + + std::mt19937& rng() { + static std::mt19937 s_rng; + return s_rng; + } + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) { + std::srand( config.rngSeed() ); + rng().seed( config.rngSeed() ); + } + } + + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } +} +// end catch_random_number_generator.cpp +// start catch_registry_hub.cpp + +// start catch_test_case_registry_impl.h + +#include +#include +#include +#include + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector const& functions ); + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector const& getAllTests() const override; + std::vector const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class TestInvokerAsFunction : public ITestInvoker { + void(*m_testAsFunction)(); + public: + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + + void invoke() const override; + }; + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ); + + /////////////////////////////////////////////////////////////////////////// + +} // end namespace Catch + +// end catch_test_case_registry_impl.h +// start catch_reporter_registry.h + +#include + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + ~ReporterRegistry() override; + + IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; + + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); + void registerListener( IReporterFactoryPtr const& factory ); + + FactoryMap const& getFactories() const override; + Listeners const& getListeners() const override; + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +// end catch_reporter_registry.h +// start catch_tag_alias_registry.h + +// start catch_tag_alias.h + +#include + +namespace Catch { + + struct TagAlias { + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); + + std::string tag; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// end catch_tag_alias.h +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; + TagAlias const* find( std::string const& alias ) const override; + std::string expandAliases( std::string const& unexpandedTestSpec ) const override; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map m_registry; + }; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include +#include + +namespace Catch { + + class StartupExceptionRegistry { + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector const& getExceptions() const noexcept; + private: + std::vector m_exceptions; + }; + +} // end namespace Catch + +// end catch_startup_exception_registry.h +// start catch_singletons.hpp + +namespace Catch { + + struct ISingleton { + virtual ~ISingleton(); + }; + + void addSingleton( ISingleton* singleton ); + void cleanupSingletons(); + + template + class Singleton : SingletonImplT, public ISingleton { + + static auto getInternal() -> Singleton* { + static Singleton* s_instance = nullptr; + if( !s_instance ) { + s_instance = new Singleton; + addSingleton( s_instance ); + } + return s_instance; + } + + public: + static auto get() -> InterfaceT const& { + return *getInternal(); + } + static auto getMutable() -> MutableInterfaceT& { + return *getInternal(); + } + }; + +} // namespace Catch + +// end catch_singletons.hpp +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private NonCopyable { + + public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry 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 const& factory ) override { + m_reporterRegistry.registerReporter( name, factory ); + } + void registerListener( IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerListener( factory ); + } + void registerTest( TestCase const& testInfo ) override { + m_testCaseRegistry.registerTest( testInfo ); + } + void registerTranslator( const IExceptionTranslator* translator ) override { + m_exceptionTranslatorRegistry.registerTranslator( 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 { + m_exceptionRegistry.add(std::current_exception()); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + }; + } + + using RegistryHubSingleton = Singleton; + + 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 +// end catch_registry_hub.cpp +// start catch_reporter_registry.cpp + +namespace Catch { + + ReporterRegistry::~ReporterRegistry() = default; + + IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( ReporterConfig( config ) ); + } + + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { + m_listeners.push_back( factory ); + } + + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; + } + +} +// end catch_reporter_registry.cpp +// start catch_result_type.cpp + +namespace Catch { + + bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include +#include +#include + +namespace Catch { + + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + {} + ~GeneratorTracker(); + + static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + tracker->open(); + } + + return *tracker; + } + + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + // Generator interface only finds out if it has another item on atual move + if (m_runState == CompletedSuccessfully && m_generator->next()) { + m_children.clear(); + m_runState = Executing; + } + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = std::move( generator ); + } + }; + GeneratorTracker::~GeneratorTracker() {} + } + + RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(std::move(reporter)), + m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, + m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) + { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const& testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + auto const& testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + ITracker& rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter& RunContext::reporter() const { + return *m_reporter; + } + + void RunContext::assertionEnded(AssertionResult const & result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } + else { + m_lastAssertionPassed = true; + } + + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + if (result.getResultType() != ResultWas::Warning) + m_messageScopes.clear(); + + // Reset working state + resetAssertionInfo(); + m_lastResult = result; + } + void RunContext::resetAssertionInfo() { + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + } + + bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + using namespace Generators; + GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); + assert( tracker.isOpen() ); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } + + bool RunContext::testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo const & endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + m_messageScopes.clear(); + } + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } + void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { + m_reporter->benchmarkEnded( stats ); + } + + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); + } + + void RunContext::popScopedMessage(MessageInfo const & message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) { + m_messageScopes.emplace_back( builder ); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult * RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition( StringRef message ) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_lastAssertionPassed; + } + + void RunContext::assertionPassed() { + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + m_messageScopes.clear(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed >= static_cast(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; + + seedRng(*m_config); + + Timer timer; + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); + + timer.start(); + invokeActiveTestCase(); +#else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); +#endif + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } CATCH_CATCH_ANON (TestFailureException&) { + // This just means the test was aborted due to failure + } CATCH_CATCH_ALL { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if( m_shouldReportUnexpected ) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); + } + } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + m_messageScopes.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + void RunContext::handleExpr( + AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + bool negated = isFalseTest( info.resultDisposition ); + bool result = expr.getResult() != negated; + + if( result ) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } + else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } + else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); + populateReaction( reaction ); + } + } + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ) { + + m_lastAssertionInfo = info; + AssertionResultData data( resultType, LazyExpression( negated ) ); + + AssertionResult assertionResult{ info, data }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded( assertionResult ); + } + + void RunContext::handleMessage( + AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ m_lastAssertionInfo, data }; + assertionEnded( assertionResult ); + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const& info, + AssertionReaction& reaction + ) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException( + AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + populateReaction( reaction ); + } + + void RunContext::populateReaction( AssertionReaction& reaction ) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::handleIncomplete( + AssertionInfo const& info + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + } + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + + IResultCapture& getResultCapture() { + if (auto* capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } +} +// end catch_run_context.cpp +// start catch_section.cpp + +namespace Catch { + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; + if( uncaught_exceptions() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch +// end catch_section.cpp +// start catch_section_info.cpp + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ) + : name( _name ), + lineInfo( _lineInfo ) + {} + +} // end namespace Catch +// end catch_section_info.cpp +// start catch_session.cpp + +// start catch_session.h + +#include + +namespace Catch { + + class Session : NonCopyable { + public: + + Session(); + ~Session() override; + + 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 + 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; + std::shared_ptr m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#include + +namespace Catch { + + // Versioning information + struct Version { + Version( Version const& ) = delete; + Version& operator=( Version const& ) = delete; + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + char const * const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator << ( std::ostream& os, Version const& version ); + }; + + Version const& libraryVersion(); +} + +// end catch_version.h +#include +#include + +namespace Catch { + + namespace { + const int MaxExitCode = 255; + + IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + + return reporter; + } + + IStreamingReporterPtr makeReporter(std::shared_ptr const& config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } + + // On older platforms, returning std::unique_ptr + // when the return type is std::unique_ptr + // doesn't compile without a std::move call. However, this causes + // a warning on newer platforms. Thus, we have to work around + // it a bit and downcast the pointer manually. + auto ret = std::unique_ptr(new ListeningReporter); + auto& multi = static_cast(*ret); + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) { + multi.addListener(listener->create(Catch::ReporterConfig(config))); + } + multi.addReporter(createReporter(config->getReporterName(), config)); + return ret; + } + + Catch::Totals runTests(std::shared_ptr const& config) { + auto reporter = makeReporter(config); + + RunContext context(config, std::move(reporter)); + + Totals totals; + + context.testGroupStarting(config->name(), 1, 1); + + TestSpec testSpec = config->testSpec(); + + auto const& allTestCases = getAllTestCasesSorted(*config); + for (auto const& testCase : allTestCases) { + if (!context.aborting() && matchTest(testCase, testSpec, *config)) + totals += context.runTest(testCase); + else + context.reporter().skipTest(testCase); + } + + if (config->warnAboutNoTests() && totals.testCases.total() == 0) { + ReusableStringStream testConfig; + + bool first = true; + for (const auto& input : config->getTestsOrTags()) { + if (!first) { testConfig << ' '; } + first = false; + testConfig << input; + } + + context.reporter().noMatchingTestCases(testConfig.str()); + totals.error = -1; + } + + context.testGroupEnded(config->name(), totals, 1, 1); + return totals; + } + + void applyFilenamesAsTags(Catch::IConfig const& config) { + auto& tests = const_cast&>(getAllTestCasesSorted(config)); + for (auto& testCase : tests) { + auto tags = testCase.tags; + + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } + } + + } // 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() ) { + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "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 ) { + Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; + } + } + } +#endif + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; + } + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } + + 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); + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" + << Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + 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, NULL, 0, NULL, NULL ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } + + 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" << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(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 = std::make_shared( m_configData ); + return *m_config; + } + + int Session::runInternal() { + if( m_startupExceptions ) + return 1; + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; + } + + CATCH_TRY { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( m_config ) ) + return static_cast( *listed ); + + auto totals = runTests( m_config ); + // 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, (std::max) (totals.error, static_cast(totals.assertions.failed))); + } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } +#endif + } + +} // end namespace Catch +// end catch_session.cpp +// start catch_singletons.cpp + +#include + +namespace Catch { + + namespace { + static auto getSingletons() -> std::vector*& { + static std::vector* g_singletons = nullptr; + if( !g_singletons ) + g_singletons = new std::vector(); + return g_singletons; + } + } + + ISingleton::~ISingleton() {} + + void addSingleton(ISingleton* singleton ) { + getSingletons()->push_back( singleton ); + } + void cleanupSingletons() { + auto& singletons = getSingletons(); + for( auto singleton : *singletons ) + delete singleton; + delete singletons; + singletons = nullptr; + } + +} // namespace Catch +// end catch_singletons.cpp +// start catch_startup_exception_registry.cpp + +namespace Catch { +void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + CATCH_TRY { + m_exceptions.push_back(exception); + } CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include +#include +#include +#include +#include +#include + +namespace Catch { + + Catch::IStream::~IStream() = default; + + namespace detail { namespace { + template + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( StringRef filename ) { + m_ofs.open( filename.c_str() ); + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); + } + ~FileStream() override = default; + public: // IStream + std::ostream& stream() const override { + return m_ofs; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() : m_os( Catch::cout().rdbuf() ) {} + ~CoutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream : public IStream { + std::unique_ptr> m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) + {} + + ~DebugOutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + }} // namespace anon::detail + + /////////////////////////////////////////////////////////////////////////// + + auto makeStream( StringRef const &filename ) -> IStream const* { + if( filename.empty() ) + return new detail::CoutStream(); + else if( filename[0] == '%' ) { + if( filename == "%debug" ) + return new detail::DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); + } + else + return new detail::FileStream( filename ); + } + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t { + if( m_unused.empty() ) { + m_streams.push_back( std::unique_ptr( new std::ostringstream ) ); + return m_streams.size()-1; + } + else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } + } + + void release( std::size_t index ) { + m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state + m_unused.push_back(index); + } + }; + + ReusableStringStream::ReusableStringStream() + : m_index( Singleton::getMutable().add() ), + m_oss( Singleton::getMutable().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast( m_oss )->str(""); + m_oss->clear(); + Singleton::getMutable().release( m_index ); + } + + auto ReusableStringStream::str() const -> std::string { + return static_cast( m_oss )->str(); + } + + /////////////////////////////////////////////////////////////////////////// + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { return std::cout; } + std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { return std::clog; } +#endif +} +// end catch_stream.cpp +// start catch_string_manip.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } + } + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + } + + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << 's'; + return os; + } + +} +// end catch_string_manip.cpp +// start catch_stringref.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +#include +#include +#include + +namespace { + const uint32_t byte_2_lead = 0xC0; + const uint32_t byte_3_lead = 0xE0; + const uint32_t byte_4_lead = 0xF0; +} + +namespace Catch { + StringRef::StringRef( char const* rawChars ) noexcept + : StringRef( rawChars, static_cast(std::strlen(rawChars) ) ) + {} + + StringRef::operator std::string() const { + return std::string( m_start, m_size ); + } + + void StringRef::swap( StringRef& other ) noexcept { + std::swap( m_start, other.m_start ); + std::swap( m_size, other.m_size ); + std::swap( m_data, other.m_data ); + } + + auto StringRef::c_str() const -> char const* { + if( isSubstring() ) + const_cast( this )->takeOwnership(); + return m_start; + } + auto StringRef::currentData() const noexcept -> char const* { + return m_start; + } + + auto StringRef::isOwned() const noexcept -> bool { + return m_data != nullptr; + } + auto StringRef::isSubstring() const noexcept -> bool { + return m_start[m_size] != '\0'; + } + + void StringRef::takeOwnership() { + if( !isOwned() ) { + m_data = new char[m_size+1]; + memcpy( m_data, m_start, m_size ); + m_data[m_size] = '\0'; + m_start = m_data; + } + } + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { + if( start < m_size ) + return StringRef( m_start+start, size ); + else + return StringRef(); + } + auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { + return + size() == other.size() && + (std::strncmp( m_start, other.m_start, size() ) == 0); + } + auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { + return !operator==( other ); + } + + auto StringRef::operator[](size_type index) const noexcept -> char { + return m_start[index]; + } + + auto StringRef::numberOfCharacters() const noexcept -> size_type { + size_type noChars = m_size; + // Make adjustments for uft encodings + for( size_type i=0; i < m_size; ++i ) { + char c = m_start[i]; + if( ( c & byte_2_lead ) == byte_2_lead ) { + noChars--; + if (( c & byte_3_lead ) == byte_3_lead ) + noChars--; + if( ( c & byte_4_lead ) == byte_4_lead ) + noChars--; + } + } + return noChars; + } + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { + std::string str; + str.reserve( lhs.size() + rhs.size() ); + str += lhs; + str += rhs; + return str; + } + auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { + return os.write(str.currentData(), str.size()); + } + + auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { + lhs.append(rhs.currentData(), rhs.size()); + return lhs; + } + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_stringref.cpp +// start catch_tag_alias.cpp + +namespace Catch { + TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} +} +// end catch_tag_alias.cpp +// start catch_tag_alias_autoregistrar.cpp + +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(); + } + } + +} +// end catch_tag_alias_autoregistrar.cpp +// start catch_tag_alias_registry.cpp + +#include + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { + auto it = m_registry.find( alias ); + if( it != m_registry.end() ) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( auto const& registryKvp : m_registry ) { + std::size_t pos = expandedTestSpec.find( registryKvp.first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + registryKvp.second.tag + + expandedTestSpec.substr( pos + registryKvp.first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + +} // end namespace Catch +// end catch_tag_alias_registry.cpp +// start catch_test_case_info.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } + bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast(tag[0]) ); + } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << _lineInfo ); + } + } + + TestCase makeTestCase( ITestInvoker* _testCase, + std::string const& _className, + NameAndTags const& nameAndTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden = false; + + // Parse out tags + std::vector tags; + std::string desc, tag; + bool inTag = false; + std::string _descOrTags = nameAndTags.tags; + for (char c : _descOrTags) { + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( ( prop & TestCaseInfo::IsHidden ) != 0 ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + tags.push_back( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.push_back( "." ); + } + + TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, std::move(info) ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); + + for( auto const& tag : tags ) { + std::string lcaseTag = toLower( tag ); + testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.push_back( lcaseTag ); + } + testCaseInfo.tags = std::move(tags); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + 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.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; + } + + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch +// end catch_test_case_info.cpp +// start catch_test_case_registry_impl.cpp + +#include + +namespace Catch { + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + seedRng( config ); + std::shuffle( sorted.begin(), sorted.end(), rng() ); + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); + } + } + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( auto const& testCase : testCases ) + if( matchTest( testCase, testSpec, config ) ) + filtered.push_back( testCase ); + return filtered; + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + void TestRegistry::registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( rss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + +} // end namespace Catch +// end catch_test_case_registry_impl.cpp +// start catch_test_case_tracker.cpp + +#include +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + + ITracker::~ITracker() = default; + + TrackerContext& TrackerContext::instance() { + static TrackerContext s_instance; + return s_instance; + } + + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } + + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } + + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } + + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& TrackerContext::currentTracker() { + return *m_currentTracker; + } + void TrackerContext::setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ) + {} + + NameAndLocation const& TrackerBase::nameAndLocation() const { + return m_nameAndLocation; + } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } + + void TrackerBase::addChild( ITrackerPtr const& child ) { + m_children.push_back( child ); + } + + ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { + auto it = std::find_if( m_children.begin(), m_children.end(), + [&nameAndLocation]( ITrackerPtr const& tracker ){ + return + tracker->nameAndLocation().location == nameAndLocation.location && + tracker->nameAndLocation().name == nameAndLocation.name; + } ); + return( it != m_children.end() ) + ? *it + : nullptr; + } + ITracker& TrackerBase::parent() { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + void TrackerBase::openChild() { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + bool TrackerBase::isSectionTracker() const { return false; } + bool TrackerBase::isGeneratorTracker() const { return false; } + + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + void TrackerBase::close() { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + + default: + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); + } + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::fail() { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } + + void TrackerBase::moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker( this ); + } + + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isComplete() const { + bool complete = true; + + if ((m_filters.empty() || m_filters[0] == "") || + std::find(m_filters.begin(), m_filters.end(), + m_nameAndLocation.name) != m_filters.end()) + complete = TrackerBase::isComplete(); + return complete; + + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast( childTracker ); + } + else { + section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void SectionTracker::addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_test_case_tracker.cpp +// start catch_test_registry.cpp + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept { + CATCH_TRY { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod ), + nameAndTags, + lineInfo)); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + AutoReg::~AutoReg() = default; +} +// end catch_test_registry.cpp +// start catch_test_spec.cpp + +#include +#include +#include +#include + +namespace Catch { + + TestSpec::Pattern::~Pattern() = default; + TestSpec::NamePattern::~NamePattern() = default; + TestSpec::TagPattern::~TagPattern() = default; + TestSpec::ExcludedPattern::~ExcludedPattern() = default; + + TestSpec::NamePattern::NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + + TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( auto const& pattern : m_patterns ) { + if( !pattern->matches( testCase ) ) + return false; + } + return true; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( auto const& filter : m_filters ) + if( filter.matches( testCase ) ) + return true; + return false; + } +} +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + + void TestSpecParser::visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void TestSpecParser::escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch +// end catch_test_spec_parser.cpp +// start catch_timer.cpp + +#include + +static const uint64_t nanosecondsInSecond = 1000000000; + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + } + + namespace { + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + auto startTime = getCurrentNanosecondsSinceEpoch(); + + for( std::size_t i = 0; i < iterations; ++i ) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while( ticks == baseTicks ); + + auto delta = ticks - baseTicks; + sum += delta; + + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / ( i + 1u ); + } + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } + } + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } + + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds()/1000; + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch +// end catch_timer.cpp +// start catch_tostring.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +// Enable specific decls locally +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#include +#include + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); + } +} + +template +std::string fpToString( T value, int precision ) { + if (Catch::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = rss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::convert(const std::string& str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; +} + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::string_view str) { + return ::Catch::Detail::stringify(std::string{ str }); +} +#endif + +std::string StringMaker::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} + +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::wstring_view str) { + return StringMaker::convert(std::wstring(str)); +} +# endif + +std::string StringMaker::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +#endif + +std::string StringMaker::convert(int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(bool b) { + return b ? "true" : "false"; +} + +std::string StringMaker::convert(signed char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker::convert(char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} + +std::string StringMaker::convert(std::nullptr_t) { + return "nullptr"; +} + +std::string StringMaker::convert(float value) { + return fpToString(value, 5) + 'f'; +} +std::string StringMaker::convert(double value) { + return fpToString(value, 10); +} + +std::string ratio_string::symbol() { return "a"; } +std::string ratio_string::symbol() { return "f"; } +std::string ratio_string::symbol() { return "p"; } +std::string ratio_string::symbol() { return "n"; } +std::string ratio_string::symbol() { return "u"; } +std::string ratio_string::symbol() { return "m"; } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_tostring.cpp +// start catch_totals.cpp + +namespace Catch { + + Counts Counts::operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + + Counts& Counts::operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; + } + + Totals Totals::operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals& Totals::operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Totals Totals::delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + +} +// end catch_totals.cpp +// start catch_uncaught_exceptions.cpp + +#include + +namespace Catch { + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif + } +} // end namespace Catch +// end catch_uncaught_exceptions.cpp +// start catch_version.cpp + +#include + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; + } + return os; + } + + Version const& libraryVersion() { + static Version version( 2, 7, 0, "", 0 ); + return version; + } + +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +#include + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( adjustCase( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + + std::string WildcardPattern::adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp + +#include + +using uchar = unsigned char; + +namespace Catch { + +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + os.flags(f); + } + +} // anonymous namespace + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void XmlEncode::encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + uchar c = m_str[idx]; + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } + + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } + + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); + break; + } + + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + uchar nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && encBytes > 2) || + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; + } + + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; + } + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << ""; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& XmlWriter::writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << ""; + m_needsNewline = true; + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + + XmlWriter& XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } +} +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp + +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result) { + result.getExpandedExpression(); + } + + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + std::sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} + + std::set TestEventListenerBase::getSupportedVerbosities() { + return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High }; + } + + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } + +} // end namespace Catch +// end catch_reporter_bases.cpp +// start catch_reporter_compact.cpp + +namespace { + +#ifdef CATCH_PLATFORM_MAC + const char* failedString() { return "FAILED"; } + const char* passedString() { return "PASSED"; } +#else + const char* failedString() { return "failed"; } + const char* passedString() { return "passed"; } +#endif + + // Colour::LightGrey + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + + std::string bothOrAll( std::size_t count ) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all " ; + } + +} // anon namespace + +namespace Catch { +namespace { +// Colour, message variants: +// - white: No tests ran. +// - red: Failed [both/all] N test cases, failed [both/all] M assertions. +// - white: Passed [both/all] N test cases (no assertions). +// - red: Failed N tests cases, failed M assertions. +// - green: Passed [both/all] N tests cases with M assertions. +void printTotals(std::ostream& out, const Totals& totals) { + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll(totals.assertions.failed) : std::string(); + out << + "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << qualify_assertions_failed << + pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << + "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + out << + "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { + Colour colour(Colour::ResultSuccess); + out << + "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << + " with " << pluralise(totals.assertions.passed, "assertion") << '.'; + } +} + +// Implementation of CompactReporter formatting +class AssertionPrinter { +public: + AssertionPrinter& operator= (AssertionPrinter const&) = delete; + AssertionPrinter(AssertionPrinter const&) = delete; + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } + +private: + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ':'; + } + + void printResultType(Colour::Code colour, std::string const& passOrFail) const { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } + + void printIssue(std::string const& issue) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) + return; + + // using messages.end() directly yields (or auto) compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; + } + + for (; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + stream << " '" << itMessage->message << '\''; + if (++itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + } + } + } + +private: + std::ostream& stream; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; +}; + +} // anon namespace + + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + ReporterPreferences CompactReporter::getPreferences() const { + return m_reporterPrefs; + } + + void CompactReporter::noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + + void CompactReporter::assertionStarting( AssertionInfo const& ) {} + + bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + + void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( stream, _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + CompactReporter::~CompactReporter() {} + + CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch +// end catch_reporter_compact.cpp +// start catch_reporter_console.cpp + +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + +namespace { + +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: + ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << '\n'; + } + printMessage(); + } + +private: + void printResultType() const { + if (!passOrFail.empty()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } + void printMessage() const { + if (!messageLabel.empty()) + stream << messageLabel << ':' << '\n'; + for (auto const& msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << Column(msg.message).indent(2) << '\n'; + } + } + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; +}; + +std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; +} + +std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; +} + +struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int width; + Justification justification; +}; +struct ColumnBreak {}; +struct RowBreak {}; + +class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + + uint64_t m_inNanoseconds; + Unit m_units; + +public: + explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), + m_units(units) { + if (m_units == Unit::Auto) { + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch (m_units) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); + default: + return static_cast(m_inNanoseconds); + } + } + auto unitsAsString() const -> std::string { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "us"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { + return os << duration.value() << " " << duration.unitsAsString(); + } +}; +} // end anon namespace + +class TablePrinter { + std::ostream& m_os; + std::vector m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + +public: + TablePrinter( std::ostream& os, std::vector columnInfos ) + : m_os( os ), + m_columnInfos( std::move( columnInfos ) ) {} + + auto columnInfos() const -> std::vector const& { + return m_columnInfos; + } + + void open() { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + for (auto const& info : m_columnInfos) + *this << info.name << ColumnBreak(); + *this << RowBreak(); + m_os << Catch::getLineOfChars<'-'>() << "\n"; + } + } + void close() { + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template + friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + // This takes account of utf8 encodings + auto strSize = Catch::StringRef(colStr).numberOfCharacters(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << "\n"; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = (strSize + 2 < static_cast(colInfo.width)) + ? std::string(colInfo.width - (strSize + 2), ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::Left) + tp.m_os << colStr << padding << " "; + else + tp.m_os << padding << colStr << " "; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << "\n"; + tp.m_currentColumn = -1; + } + return tp; + } +}; + +ConsoleReporter::ConsoleReporter(ReporterConfig const& config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter(config.stream(), + { + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, + { "iters", 8, ColumnInfo::Right }, + { "elapsed ns", 14, ColumnInfo::Right }, + { "average", 14, ColumnInfo::Right } + })) {} +ConsoleReporter::~ConsoleReporter() = default; + +std::string ConsoleReporter::getDescription() { + return "Reports test results as plain lines of text"; +} + +void ConsoleReporter::noMatchingTestCases(std::string const& spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; +} + +void ConsoleReporter::assertionStarting(AssertionInfo const&) {} + +bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return false; + + lazyPrint(); + + ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); + printer.print(); + stream << std::endl; + return true; +} + +void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); +} + +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = Column( info.name ).width( static_cast( m_tablePrinter->columnInfos()[0].width - 2 ) ); + + bool firstLine = true; + for (auto line : nameCol) { + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; + + (*m_tablePrinter) << line << ColumnBreak(); + } +} +void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) { + Duration average(stats.elapsedTimeInNanoseconds / stats.iterations); + (*m_tablePrinter) + << stats.iterations << ColumnBreak() + << stats.elapsedTimeInNanoseconds << ColumnBreak() + << average << ColumnBreak(); +} + +void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; +} +void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) { + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); +} +void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); +} + +void ConsoleReporter::lazyPrint() { + + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); +} + +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); + + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } +} +void ConsoleReporter::lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; + + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; +} +void ConsoleReporter::lazyPrintGroupInfo() { + if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } +} +void ConsoleReporter::printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); + + auto + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + if (!lineInfo.empty()) { + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + } + stream << getLineOfChars<'.'>() << '\n' << std::endl; +} + +void ConsoleReporter::printClosedHeader(std::string const& _name) { + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const& _name) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } +} + +// if string has a : in first line will set indent to follow it on +// subsequent lines +void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; +} + +struct SummaryColumn { + + SummaryColumn( std::string _label, Colour::Code _colour ) + : label( std::move( _label ) ), + colour( _colour ) {} + SummaryColumn addRow( std::size_t count ) { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto& oldRow : rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + +}; + +void ConsoleReporter::printTotals( Totals const& totals ) { + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ')' + << '\n'; + } else { + + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } +} +void ConsoleReporter::printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row) { + for (auto col : cols) { + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) + << value << ' ' << col.label; + } + } + stream << '\n'; +} + +void ConsoleReporter::printTotalsDivider(Totals const& totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; +} + +CATCH_REGISTER_REPORTER("console", ConsoleReporter) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_console.cpp +// start catch_reporter_junit.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + std::string fileNameTag(const std::vector &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (std::string const& tag) {return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } + } // anonymous namespace + + JunitReporter::JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + JunitReporter::~JunitReporter() {} + + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {} + + void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + if( m_config->rngSeed() != 0 ) { + xml.startElement( "properties" ); + xml.scopedElement( "property" ) + .writeAttribute( "name", "random-seed" ) + .writeAttribute( "value", m_config->rngSeed() ); + xml.endElement(); + } + } + + void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) { + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { + m_okToFail = testCaseInfo.okToFail(); + } + + bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + void JunitReporter::testRunEndedCumulative() { + xml.endElement(); + } + + void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write test cases + for( auto const& child : groupNode.children ) + writeTestCase( *child ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); + } + + void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + className = fileNameTag(stats.testInfo.tags); + if ( className.empty() ) + className = "global"; + } + + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; + + writeSection( className, "", rootSection ); + } + + void JunitReporter::writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) + writeSection( name, "", *childNode ); + else + writeSection( className, name, *childNode ); + } + + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { + for( auto const& assertion : sectionNode.assertions ) + writeAssertion( assertion ); + } + + void JunitReporter::writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + ReusableStringStream rss; + if( !result.getMessage().empty() ) + rss << result.getMessage() << '\n'; + for( auto const& msg : stats.infoMessages ) + if( msg.type == ResultWas::Info ) + rss << msg.message << '\n'; + + rss << "at " << result.getSourceInfo(); + xml.writeText( rss.str(), false ); + } + } + + CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch +// end catch_reporter_junit.cpp +// start catch_reporter_listening.cpp + +#include + +namespace Catch { + + ListeningReporter::ListeningReporter() { + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; + } + + void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { + m_listeners.push_back( std::move( listener ) ); + } + + void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) { + assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move( reporter ); + m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; + } + + ReporterPreferences ListeningReporter::getPreferences() const { + return m_preferences; + } + + std::set ListeningReporter::getSupportedVerbosities() { + return std::set{ }; + } + + void ListeningReporter::noMatchingTestCases( std::string const& spec ) { + for ( auto const& listener : m_listeners ) { + listener->noMatchingTestCases( spec ); + } + m_reporter->noMatchingTestCases( spec ); + } + + void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkStarting( benchmarkInfo ); + } + m_reporter->benchmarkStarting( benchmarkInfo ); + } + void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkEnded( benchmarkStats ); + } + m_reporter->benchmarkEnded( benchmarkStats ); + } + + void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testRunStarting( testRunInfo ); + } + m_reporter->testRunStarting( testRunInfo ); + } + + void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupStarting( groupInfo ); + } + m_reporter->testGroupStarting( groupInfo ); + } + + void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseStarting( testInfo ); + } + m_reporter->testCaseStarting( testInfo ); + } + + void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->sectionStarting( sectionInfo ); + } + m_reporter->sectionStarting( sectionInfo ); + } + + void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->assertionStarting( assertionInfo ); + } + m_reporter->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) { + for( auto const& listener : m_listeners ) { + static_cast( listener->assertionEnded( assertionStats ) ); + } + return m_reporter->assertionEnded( assertionStats ); + } + + void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) { + for ( auto const& listener : m_listeners ) { + listener->sectionEnded( sectionStats ); + } + m_reporter->sectionEnded( sectionStats ); + } + + void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseEnded( testCaseStats ); + } + m_reporter->testCaseEnded( testCaseStats ); + } + + void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupEnded( testGroupStats ); + } + m_reporter->testGroupEnded( testGroupStats ); + } + + void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) { + for ( auto const& listener : m_listeners ) { + listener->testRunEnded( testRunStats ); + } + m_reporter->testRunEnded( testRunStats ); + } + + void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->skipTest( testInfo ); + } + m_reporter->skipTest( testInfo ); + } + + bool ListeningReporter::isMulti() const { + return true; + } + +} // end namespace Catch +// end catch_reporter_listening.cpp +// start catch_reporter_xml.cpp + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + XmlReporter::XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + XmlReporter::~XmlReporter() = default; + + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } + + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } + + void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + + void XmlReporter::noMatchingTestCases( std::string const& s ) { + StreamingReporterBase::noMatchingTestCases( s ); + } + + void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + if( m_config->rngSeed() != 0 ) + m_xml.scopedElement( "Randomness" ) + .writeAttribute( "seed", m_config->rngSeed() ); + } + + void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString() ); + + writeSourceInfo( testInfo.lineInfo ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); + } + } + + void XmlReporter::assertionStarting( AssertionInfo const& ) { } + + bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { + + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in tags. + for( auto const& msg : assertionStats.infoMessages ) { + if( msg.type == ResultWas::Info && includeResults ) { + m_xml.scopedElement( "Info" ) + .writeText( msg.message ); + } else if ( msg.type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( msg.message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return true; + + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); + + m_xml.scopedElement( "Original" ) + .writeText( result.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( result.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( result.getResultType() ) { + case ResultWas::ThrewException: + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( result.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + default: + break; + } + + if( result.hasExpression() ) + m_xml.endElement(); + + return true; + } + + void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + + m_xml.endElement(); + } + + void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_xml.cpp + +namespace Catch { + LeakDetector leakDetector; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_impl.hpp +#endif + +#ifdef CATCH_CONFIG_MAIN +// start catch_default_main.hpp + +#ifndef __OBJC__ + +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else +// Standard C/C++ main entry point +int main (int argc, char * argv[]) { +#endif + + return Catch::Session().run( argc, argv ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char**)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +// end catch_default_main.hpp +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +#if !defined(CATCH_CONFIG_DISABLE) +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#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, __VA_ARGS__ ) +#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_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__ ) + +#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_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_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_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( 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_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#endif + +#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__ ) +#else +#define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_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 ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#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, __VA_ARGS__ ) +#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#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__ ) + +#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 ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_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_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( 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_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#endif + +#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__ ")" ) +#else +#define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) +#endif + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "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 ) + +using Catch::Detail::Approx; + +#else // CATCH_CONFIG_DISABLE + +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#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_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#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_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) + +#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) (void)(0) +#define CATCH_WARN( msg ) (void)(0) +#define CATCH_CAPTURE( msg ) (void)(0) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#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_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#endif + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), 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 ) + +#define CATCH_STATIC_REQUIRE( ... ) (void)(0) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) (void)(0) +#define REQUIRE_FALSE( ... ) (void)(0) + +#define REQUIRE_THROWS( ... ) (void)(0) +#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#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_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) (void)(0) + +#define REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) (void)(0) +#define WARN( msg ) (void)(0) +#define CAPTURE( msg ) (void)(0) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#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 ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#endif + +#define STATIC_REQUIRE( ... ) (void)(0) +#define STATIC_REQUIRE_FALSE( ... ) (void)(0) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) + +#define GIVEN( desc ) +#define AND_GIVEN( desc ) +#define WHEN( desc ) +#define AND_WHEN( desc ) +#define THEN( desc ) +#define AND_THEN( desc ) + +using Catch::Detail::Approx; + +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +// start catch_reenable_warnings.h + + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + +// end catch_reenable_warnings.h +// end catch.hpp +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/cores/test/include/Arduino.h b/cores/test/include/Arduino.h new file mode 100644 index 00000000..7d2fd22e --- /dev/null +++ b/cores/test/include/Arduino.h @@ -0,0 +1,26 @@ +/* + Copyright (c) 2019 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ARDUINO_H_ +#define ARDUINO_H_ + +/* This is a Arduino.h mockup for test purposes only */ + +#include + +#endif /* ARDUINO_H_ */ diff --git a/cores/test/src/test_main.cpp b/cores/test/src/test_main.cpp new file mode 100644 index 00000000..4b0d36da --- /dev/null +++ b/cores/test/src/test_main.cpp @@ -0,0 +1,294 @@ +/* + Copyright (c) 2019 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#define CATCH_CONFIG_MAIN +#include + +#include "../../arduino/NANO_compat.h" + +/*****************************************************************************/ + +SCENARIO("Testing Arduino Nano 4809 DDRB compatibility class", "NANO_compat::DDRBClass") { + PORT_t portb, porte; + + DDRBClass DDRB(&portb, &porte); + + REQUIRE(portb.DIR.val() == 0); + REQUIRE(porte.DIR.val() == 0); + + GIVEN("Testing operator = ") { + WHEN("ATMEGA328P DDRB = (1<<0)") { DDRB = (1<<0); THEN("ATMEGA4809 PORTE.DIR = (1<<3)") REQUIRE(porte.DIR.val() == (1<<3)); } + WHEN("ATMEGA328P DDRB = (1<<1)") { DDRB = (1<<1); THEN("ATMEGA4809 PORTB.DIR = (1<<0)") REQUIRE(portb.DIR.val() == (1<<0)); } + WHEN("ATMEGA328P DDRB = (1<<2)") { DDRB = (1<<2); THEN("ATMEGA4809 PORTB.DIR = (1<<1)") REQUIRE(portb.DIR.val() == (1<<1)); } + WHEN("ATMEGA328P DDRB = (1<<3)") { DDRB = (1<<3); THEN("ATMEGA4809 PORTE.DIR = (1<<0)") REQUIRE(porte.DIR.val() == (1<<0)); } + WHEN("ATMEGA328P DDRB = (1<<4)") { DDRB = (1<<4); THEN("ATMEGA4809 PORTE.DIR = (1<<1)") REQUIRE(porte.DIR.val() == (1<<1)); } + WHEN("ATMEGA328P DDRB = (1<<5)") { DDRB = (1<<5); THEN("ATMEGA4809 PORTE.DIR = (1<<2)") REQUIRE(porte.DIR.val() == (1<<2)); } + } + + GIVEN("Testing operator &= ") { + portb.DIR.set(0xFF); + porte.DIR.set(0xFF); + WHEN("ATMEGA328P DDRB &= ~(1<<0)") { DDRB &= ~(1<<0); THEN("ATMEGA4809 PORTE.DIR(3) = 0") REQUIRE(porte.DIR.val() == 0xF7); } + WHEN("ATMEGA328P DDRB &= ~(1<<1)") { DDRB &= ~(1<<1); THEN("ATMEGA4809 PORTB.DIR(0) = 0") REQUIRE(portb.DIR.val() == 0xFE); } + WHEN("ATMEGA328P DDRB &= ~(1<<2)") { DDRB &= ~(1<<2); THEN("ATMEGA4809 PORTB.DIR(1) = 0") REQUIRE(portb.DIR.val() == 0xFD); } + WHEN("ATMEGA328P DDRB &= ~(1<<3)") { DDRB &= ~(1<<3); THEN("ATMEGA4809 PORTE.DIR(0) = 0") REQUIRE(porte.DIR.val() == 0xFE); } + WHEN("ATMEGA328P DDRB &= ~(1<<4)") { DDRB &= ~(1<<4); THEN("ATMEGA4809 PORTE.DIR(1) = 0") REQUIRE(porte.DIR.val() == 0xFD); } + WHEN("ATMEGA328P DDRB &= ~(1<<5)") { DDRB &= ~(1<<5); THEN("ATMEGA4809 PORTE.DIR(2) = 0") REQUIRE(porte.DIR.val() == 0xFB); } + } + + GIVEN("Testing operator |= ") { + WHEN("ATMEGA328P DDRB |= (1<<0)") { DDRB |= (1<<0); THEN("ATMEGA4809 PORTE.DIR = (1<<3)") REQUIRE(porte.DIR.val() == (1<<3)); } + WHEN("ATMEGA328P DDRB |= (1<<1)") { DDRB |= (1<<1); THEN("ATMEGA4809 PORTB.DIR = (1<<0)") REQUIRE(portb.DIR.val() == (1<<0)); } + WHEN("ATMEGA328P DDRB |= (1<<2)") { DDRB |= (1<<2); THEN("ATMEGA4809 PORTB.DIR = (1<<1)") REQUIRE(portb.DIR.val() == (1<<1)); } + WHEN("ATMEGA328P DDRB |= (1<<3)") { DDRB |= (1<<3); THEN("ATMEGA4809 PORTE.DIR = (1<<0)") REQUIRE(porte.DIR.val() == (1<<0)); } + WHEN("ATMEGA328P DDRB |= (1<<4)") { DDRB |= (1<<4); THEN("ATMEGA4809 PORTE.DIR = (1<<1)") REQUIRE(porte.DIR.val() == (1<<1)); } + WHEN("ATMEGA328P DDRB |= (1<<5)") { DDRB |= (1<<5); THEN("ATMEGA4809 PORTE.DIR = (1<<2)") REQUIRE(porte.DIR.val() == (1<<2)); } + } +} + +/*****************************************************************************/ + +SCENARIO("Testing Arduino Nano 4809 PORTB compatibility class", "NANO_compat::PORTBClass") { + PORT_t portb, porte; + + PORTBClass PORTB(&portb, &porte); + + REQUIRE(portb.OUT.val() == 0); + REQUIRE(porte.OUT.val() == 0); + + GIVEN("Testing operator = ") { + WHEN("ATMEGA328P PORTB = (1<<0)") { PORTB = (1<<0); THEN("ATMEGA4809 PORTE.OUT = (1<<3)") REQUIRE(porte.OUT.val() == (1<<3)); } + WHEN("ATMEGA328P PORTB = (1<<1)") { PORTB = (1<<1); THEN("ATMEGA4809 PORTB.OUT = (1<<0)") REQUIRE(portb.OUT.val() == (1<<0)); } + WHEN("ATMEGA328P PORTB = (1<<2)") { PORTB = (1<<2); THEN("ATMEGA4809 PORTB.OUT = (1<<1)") REQUIRE(portb.OUT.val() == (1<<1)); } + WHEN("ATMEGA328P PORTB = (1<<3)") { PORTB = (1<<3); THEN("ATMEGA4809 PORTE.OUT = (1<<0)") REQUIRE(porte.OUT.val() == (1<<0)); } + WHEN("ATMEGA328P PORTB = (1<<4)") { PORTB = (1<<4); THEN("ATMEGA4809 PORTE.OUT = (1<<1)") REQUIRE(porte.OUT.val() == (1<<1)); } + WHEN("ATMEGA328P PORTB = (1<<5)") { PORTB = (1<<5); THEN("ATMEGA4809 PORTE.OUT = (1<<2)") REQUIRE(porte.OUT.val() == (1<<2)); } + } + + GIVEN("Testing operator &= ") { + portb.OUT.set(0xFF); + porte.OUT.set(0xFF); + WHEN("ATMEGA328P PORTB &= ~(1<<0)") { PORTB &= ~(1<<0); THEN("ATMEGA4809 PORTE.OUT(3) = 0") REQUIRE(porte.OUT.val() == 0xF7); } + WHEN("ATMEGA328P PORTB &= ~(1<<1)") { PORTB &= ~(1<<1); THEN("ATMEGA4809 PORTB.OUT(0) = 0") REQUIRE(portb.OUT.val() == 0xFE); } + WHEN("ATMEGA328P PORTB &= ~(1<<2)") { PORTB &= ~(1<<2); THEN("ATMEGA4809 PORTB.OUT(1) = 0") REQUIRE(portb.OUT.val() == 0xFD); } + WHEN("ATMEGA328P PORTB &= ~(1<<3)") { PORTB &= ~(1<<3); THEN("ATMEGA4809 PORTE.OUT(0) = 0") REQUIRE(porte.OUT.val() == 0xFE); } + WHEN("ATMEGA328P PORTB &= ~(1<<4)") { PORTB &= ~(1<<4); THEN("ATMEGA4809 PORTE.OUT(1) = 0") REQUIRE(porte.OUT.val() == 0xFD); } + WHEN("ATMEGA328P PORTB &= ~(1<<5)") { PORTB &= ~(1<<5); THEN("ATMEGA4809 PORTE.OUT(2) = 0") REQUIRE(porte.OUT.val() == 0xFB); } + } + + GIVEN("Testing operator |= ") { + WHEN("ATMEGA328P PORTB |= (1<<0)") { PORTB |= (1<<0); THEN("ATMEGA4809 PORTE.OUT = (1<<3)") REQUIRE(porte.OUT.val() == (1<<3)); } + WHEN("ATMEGA328P PORTB |= (1<<1)") { PORTB |= (1<<1); THEN("ATMEGA4809 PORTB.OUT = (1<<0)") REQUIRE(portb.OUT.val() == (1<<0)); } + WHEN("ATMEGA328P PORTB |= (1<<2)") { PORTB |= (1<<2); THEN("ATMEGA4809 PORTB.OUT = (1<<1)") REQUIRE(portb.OUT.val() == (1<<1)); } + WHEN("ATMEGA328P PORTB |= (1<<3)") { PORTB |= (1<<3); THEN("ATMEGA4809 PORTE.OUT = (1<<0)") REQUIRE(porte.OUT.val() == (1<<0)); } + WHEN("ATMEGA328P PORTB |= (1<<4)") { PORTB |= (1<<4); THEN("ATMEGA4809 PORTE.OUT = (1<<1)") REQUIRE(porte.OUT.val() == (1<<1)); } + WHEN("ATMEGA328P PORTB |= (1<<5)") { PORTB |= (1<<5); THEN("ATMEGA4809 PORTE.OUT = (1<<2)") REQUIRE(porte.OUT.val() == (1<<2)); } + } +} + +/*****************************************************************************/ + +SCENARIO("Testing Arduino Nano 4809 DDRC compatibility class", "NANO_compat::DDRCClass") { + PORT_t porta, portd; + + DDRCClass DDRC(&porta, &portd); + + REQUIRE(porta.DIR.val() == 0); + REQUIRE(portd.DIR.val() == 0); + + GIVEN("Testing operator = ") { + WHEN("ATMEGA328P DDRC = (1<<0)") { DDRC = (1<<0); THEN("ATMEGA4809 PORTD.DIR = (1<<3)") REQUIRE(portd.DIR.val() == (1<<3)); } + WHEN("ATMEGA328P DDRC = (1<<1)") { DDRC = (1<<1); THEN("ATMEGA4809 PORTD.DIR = (1<<2)") REQUIRE(portd.DIR.val() == (1<<2)); } + WHEN("ATMEGA328P DDRC = (1<<2)") { DDRC = (1<<2); THEN("ATMEGA4809 PORTD.DIR = (1<<1)") REQUIRE(portd.DIR.val() == (1<<1)); } + WHEN("ATMEGA328P DDRC = (1<<3)") { DDRC = (1<<3); THEN("ATMEGA4809 PORTD.DIR = (1<<0)") REQUIRE(portd.DIR.val() == (1<<0)); } + WHEN("ATMEGA328P DDRC = (1<<4)") { DDRC = (1<<4); THEN("ATMEGA4809 PORTA.DIR = (1<<2)") REQUIRE(porta.DIR.val() == (1<<2)); } + WHEN("ATMEGA328P DDRC = (1<<5)") { DDRC = (1<<5); THEN("ATMEGA4809 PORTA.DIR = (1<<3)") REQUIRE(porta.DIR.val() == (1<<3)); } + WHEN("ATMEGA328P DDRC = (1<<6)") { DDRC = (1<<6); THEN("ATMEGA4809 PORTD.DIR = (1<<4)") REQUIRE(portd.DIR.val() == (1<<4)); } + WHEN("ATMEGA328P DDRC = (1<<7)") { DDRC = (1<<7); THEN("ATMEGA4809 PORTD.DIR = (1<<5)") REQUIRE(portd.DIR.val() == (1<<5)); } + } + + GIVEN("Testing operator &= ") { + porta.DIR.set(0xFF); + portd.DIR.set(0xFF); + WHEN("ATMEGA328P DDRC &= ~(1<<0)") { DDRC &= ~(1<<0); THEN("ATMEGA4809 PORTD.DIR = 0xF7") REQUIRE(portd.DIR.val() == 0xF7); } + WHEN("ATMEGA328P DDRC &= ~(1<<1)") { DDRC &= ~(1<<1); THEN("ATMEGA4809 PORTD.DIR = 0xFB") REQUIRE(portd.DIR.val() == 0xFB); } + WHEN("ATMEGA328P DDRC &= ~(1<<2)") { DDRC &= ~(1<<2); THEN("ATMEGA4809 PORTD.DIR = 0xFD") REQUIRE(portd.DIR.val() == 0xFD); } + WHEN("ATMEGA328P DDRC &= ~(1<<3)") { DDRC &= ~(1<<3); THEN("ATMEGA4809 PORTD.DIR = 0xFE") REQUIRE(portd.DIR.val() == 0xFE); } + WHEN("ATMEGA328P DDRC &= ~(1<<4)") { DDRC &= ~(1<<4); THEN("ATMEGA4809 PORTA.DIR = 0xFB") REQUIRE(porta.DIR.val() == 0xFB); } + WHEN("ATMEGA328P DDRC &= ~(1<<5)") { DDRC &= ~(1<<5); THEN("ATMEGA4809 PORTA.DIR = 0xF7") REQUIRE(porta.DIR.val() == 0xF7); } + WHEN("ATMEGA328P DDRC &= ~(1<<6)") { DDRC &= ~(1<<6); THEN("ATMEGA4809 PORTD.DIR = 0xFE") REQUIRE(portd.DIR.val() == 0xEF); } + WHEN("ATMEGA328P DDRC &= ~(1<<7)") { DDRC &= ~(1<<7); THEN("ATMEGA4809 PORTD.DIR = 0xDF") REQUIRE(portd.DIR.val() == 0xDF); } + } + + GIVEN("Testing operator |= ") { + WHEN("ATMEGA328P DDRC |= (1<<0)") { DDRC |= (1<<0); THEN("ATMEGA4809 PORTD.DIR = (1<<3)") REQUIRE(portd.DIR.val() == (1<<3)); } + WHEN("ATMEGA328P DDRC |= (1<<1)") { DDRC |= (1<<1); THEN("ATMEGA4809 PORTD.DIR = (1<<2)") REQUIRE(portd.DIR.val() == (1<<2)); } + WHEN("ATMEGA328P DDRC |= (1<<2)") { DDRC |= (1<<2); THEN("ATMEGA4809 PORTD.DIR = (1<<1)") REQUIRE(portd.DIR.val() == (1<<1)); } + WHEN("ATMEGA328P DDRC |= (1<<3)") { DDRC |= (1<<3); THEN("ATMEGA4809 PORTD.DIR = (1<<0)") REQUIRE(portd.DIR.val() == (1<<0)); } + WHEN("ATMEGA328P DDRC |= (1<<4)") { DDRC |= (1<<4); THEN("ATMEGA4809 PORTA.DIR = (1<<2)") REQUIRE(porta.DIR.val() == (1<<2)); } + WHEN("ATMEGA328P DDRC |= (1<<5)") { DDRC |= (1<<5); THEN("ATMEGA4809 PORTA.DIR = (1<<3)") REQUIRE(porta.DIR.val() == (1<<3)); } + WHEN("ATMEGA328P DDRC |= (1<<6)") { DDRC |= (1<<6); THEN("ATMEGA4809 PORTD.DIR = (1<<4)") REQUIRE(portd.DIR.val() == (1<<4)); } + WHEN("ATMEGA328P DDRC |= (1<<7)") { DDRC |= (1<<7); THEN("ATMEGA4809 PORTD.DIR = (1<<5)") REQUIRE(portd.DIR.val() == (1<<5)); } + } +} + +/*****************************************************************************/ + +SCENARIO("Testing Arduino Nano 4809 PORTC compatibility class", "NANO_compat::PORTCClass") { + PORT_t porta, portd; + + PORTCClass PORTC(&porta, &portd); + + REQUIRE(porta.OUT.val() == 0); + REQUIRE(portd.OUT.val() == 0); + + GIVEN("Testing operator = ") { + WHEN("ATMEGA328P PORTC = (1<<0)") { PORTC = (1<<0); THEN("ATMEGA4809 PORTD.OUT = (1<<3)") REQUIRE(portd.OUT.val() == (1<<3)); } + WHEN("ATMEGA328P PORTC = (1<<1)") { PORTC = (1<<1); THEN("ATMEGA4809 PORTD.OUT = (1<<2)") REQUIRE(portd.OUT.val() == (1<<2)); } + WHEN("ATMEGA328P PORTC = (1<<2)") { PORTC = (1<<2); THEN("ATMEGA4809 PORTD.OUT = (1<<1)") REQUIRE(portd.OUT.val() == (1<<1)); } + WHEN("ATMEGA328P PORTC = (1<<3)") { PORTC = (1<<3); THEN("ATMEGA4809 PORTD.OUT = (1<<0)") REQUIRE(portd.OUT.val() == (1<<0)); } + WHEN("ATMEGA328P PORTC = (1<<4)") { PORTC = (1<<4); THEN("ATMEGA4809 PORTA.OUT = (1<<2)") REQUIRE(porta.OUT.val() == (1<<2)); } + WHEN("ATMEGA328P PORTC = (1<<5)") { PORTC = (1<<5); THEN("ATMEGA4809 PORTA.OUT = (1<<3)") REQUIRE(porta.OUT.val() == (1<<3)); } + WHEN("ATMEGA328P PORTC = (1<<6)") { PORTC = (1<<6); THEN("ATMEGA4809 PORTD.OUT = (1<<4)") REQUIRE(portd.OUT.val() == (1<<4)); } + WHEN("ATMEGA328P PORTC = (1<<7)") { PORTC = (1<<7); THEN("ATMEGA4809 PORTD.OUT = (1<<5)") REQUIRE(portd.OUT.val() == (1<<5)); } + } + + GIVEN("Testing operator &= ") { + porta.OUT.set(0xFF); + portd.OUT.set(0xFF); + WHEN("ATMEGA328P PORTC &= ~(1<<0)") { PORTC &= ~(1<<0); THEN("ATMEGA4809 PORTD.OUT = 0xF7") REQUIRE(portd.OUT.val() == 0xF7); } + WHEN("ATMEGA328P PORTC &= ~(1<<1)") { PORTC &= ~(1<<1); THEN("ATMEGA4809 PORTD.OUT = 0xFB") REQUIRE(portd.OUT.val() == 0xFB); } + WHEN("ATMEGA328P PORTC &= ~(1<<2)") { PORTC &= ~(1<<2); THEN("ATMEGA4809 PORTD.OUT = 0xFD") REQUIRE(portd.OUT.val() == 0xFD); } + WHEN("ATMEGA328P PORTC &= ~(1<<3)") { PORTC &= ~(1<<3); THEN("ATMEGA4809 PORTD.OUT = 0xFE") REQUIRE(portd.OUT.val() == 0xFE); } + WHEN("ATMEGA328P PORTC &= ~(1<<4)") { PORTC &= ~(1<<4); THEN("ATMEGA4809 PORTA.OUT = 0xFB") REQUIRE(porta.OUT.val() == 0xFB); } + WHEN("ATMEGA328P PORTC &= ~(1<<5)") { PORTC &= ~(1<<5); THEN("ATMEGA4809 PORTA.OUT = 0xF7") REQUIRE(porta.OUT.val() == 0xF7); } + WHEN("ATMEGA328P PORTC &= ~(1<<6)") { PORTC &= ~(1<<6); THEN("ATMEGA4809 PORTD.OUT = 0xFE") REQUIRE(portd.OUT.val() == 0xEF); } + WHEN("ATMEGA328P PORTC &= ~(1<<7)") { PORTC &= ~(1<<7); THEN("ATMEGA4809 PORTD.OUT = 0xDF") REQUIRE(portd.OUT.val() == 0xDF); } + } + + GIVEN("Testing operator |= ") { + WHEN("ATMEGA328P PORTC |= (1<<0)") { PORTC |= (1<<0); THEN("ATMEGA4809 PORTD.OUT = (1<<3)") REQUIRE(portd.OUT.val() == (1<<3)); } + WHEN("ATMEGA328P PORTC |= (1<<1)") { PORTC |= (1<<1); THEN("ATMEGA4809 PORTD.OUT = (1<<2)") REQUIRE(portd.OUT.val() == (1<<2)); } + WHEN("ATMEGA328P PORTC |= (1<<2)") { PORTC |= (1<<2); THEN("ATMEGA4809 PORTD.OUT = (1<<1)") REQUIRE(portd.OUT.val() == (1<<1)); } + WHEN("ATMEGA328P PORTC |= (1<<3)") { PORTC |= (1<<3); THEN("ATMEGA4809 PORTD.OUT = (1<<0)") REQUIRE(portd.OUT.val() == (1<<0)); } + WHEN("ATMEGA328P PORTC |= (1<<4)") { PORTC |= (1<<4); THEN("ATMEGA4809 PORTA.OUT = (1<<2)") REQUIRE(porta.OUT.val() == (1<<2)); } + WHEN("ATMEGA328P PORTC |= (1<<5)") { PORTC |= (1<<5); THEN("ATMEGA4809 PORTA.OUT = (1<<3)") REQUIRE(porta.OUT.val() == (1<<3)); } + WHEN("ATMEGA328P PORTC |= (1<<6)") { PORTC |= (1<<6); THEN("ATMEGA4809 PORTD.OUT = (1<<4)") REQUIRE(portd.OUT.val() == (1<<4)); } + WHEN("ATMEGA328P PORTC |= (1<<7)") { PORTC |= (1<<7); THEN("ATMEGA4809 PORTD.OUT = (1<<5)") REQUIRE(portd.OUT.val() == (1<<5)); } + } +} + +/*****************************************************************************/ + +SCENARIO("Testing Arduino Nano 4809 DDRD compatibility class", "NANO_compat::DDRDClass") { + PORT_t porta, portb, portc, portf; + + DDRDClass DDRD(&porta, &portb, &portc, &portf); + + REQUIRE(porta.DIR.val() == 0); + REQUIRE(portb.DIR.val() == 0); + REQUIRE(portc.DIR.val() == 0); + REQUIRE(portf.DIR.val() == 0); + + GIVEN("Testing operator = ") { + WHEN("ATMEGA328P DDRD = (1<<0)") { DDRD = (1<<0); THEN("ATMEGA4809 PORTC.DIR = (1<<4)") REQUIRE(portc.DIR.val() == (1<<4)); } + WHEN("ATMEGA328P DDRD = (1<<1)") { DDRD = (1<<1); THEN("ATMEGA4809 PORTC.DIR = (1<<5)") REQUIRE(portc.DIR.val() == (1<<5)); } + WHEN("ATMEGA328P DDRD = (1<<2)") { DDRD = (1<<2); THEN("ATMEGA4809 PORTA.DIR = (1<<0)") REQUIRE(porta.DIR.val() == (1<<0)); } + WHEN("ATMEGA328P DDRD = (1<<3)") { DDRD = (1<<3); THEN("ATMEGA4809 PORTF.DIR = (1<<5)") REQUIRE(portf.DIR.val() == (1<<5)); } + WHEN("ATMEGA328P DDRD = (1<<4)") { DDRD = (1<<4); THEN("ATMEGA4809 PORTC.DIR = (1<<6)") REQUIRE(portc.DIR.val() == (1<<6)); } + WHEN("ATMEGA328P DDRD = (1<<5)") { DDRD = (1<<5); THEN("ATMEGA4809 PORTB.DIR = (1<<2)") REQUIRE(portb.DIR.val() == (1<<2)); } + WHEN("ATMEGA328P DDRD = (1<<6)") { DDRD = (1<<6); THEN("ATMEGA4809 PORTF.DIR = (1<<4)") REQUIRE(portf.DIR.val() == (1<<4)); } + WHEN("ATMEGA328P DDRD = (1<<7)") { DDRD = (1<<7); THEN("ATMEGA4809 PORTA.DIR = (1<<1)") REQUIRE(porta.DIR.val() == (1<<1)); } + } + + GIVEN("Testing operator &= ") { + porta.DIR.set(0xFF); + portb.DIR.set(0xFF); + portc.DIR.set(0xFF); + portf.DIR.set(0xFF); + WHEN("ATMEGA328P DDRD &= ~(1<<0)") { DDRD &= ~(1<<0); THEN("ATMEGA4809 PORTC.DIR = 0xEF") REQUIRE(portc.DIR.val() == 0xEF); } + WHEN("ATMEGA328P DDRD &= ~(1<<1)") { DDRD &= ~(1<<1); THEN("ATMEGA4809 PORTC.DIR = 0xDF") REQUIRE(portc.DIR.val() == 0xDF); } + WHEN("ATMEGA328P DDRD &= ~(1<<2)") { DDRD &= ~(1<<2); THEN("ATMEGA4809 PORTA.DIR = 0xFE") REQUIRE(porta.DIR.val() == 0xFE); } + WHEN("ATMEGA328P DDRD &= ~(1<<3)") { DDRD &= ~(1<<3); THEN("ATMEGA4809 PORTF.DIR = 0xDF") REQUIRE(portf.DIR.val() == 0xDF); } + WHEN("ATMEGA328P DDRD &= ~(1<<4)") { DDRD &= ~(1<<4); THEN("ATMEGA4809 PORTC.DIR = 0xBF") REQUIRE(portc.DIR.val() == 0xBF); } + WHEN("ATMEGA328P DDRD &= ~(1<<5)") { DDRD &= ~(1<<5); THEN("ATMEGA4809 PORTB.DIR = 0xFB") REQUIRE(portb.DIR.val() == 0xFB); } + WHEN("ATMEGA328P DDRD &= ~(1<<6)") { DDRD &= ~(1<<6); THEN("ATMEGA4809 PORTF.DIR = 0xEF") REQUIRE(portf.DIR.val() == 0xEF); } + WHEN("ATMEGA328P DDRD &= ~(1<<7)") { DDRD &= ~(1<<7); THEN("ATMEGA4809 PORTA.DIR = 0xFD") REQUIRE(porta.DIR.val() == 0xFD); } + } + + GIVEN("Testing operator |= ") { + WHEN("ATMEGA328P DDRD |= (1<<0)") { DDRD |= (1<<0); THEN("ATMEGA4809 PORTC.DIR = (1<<4)") REQUIRE(portc.DIR.val() == (1<<4)); } + WHEN("ATMEGA328P DDRD |= (1<<1)") { DDRD |= (1<<1); THEN("ATMEGA4809 PORTC.DIR = (1<<5)") REQUIRE(portc.DIR.val() == (1<<5)); } + WHEN("ATMEGA328P DDRD |= (1<<2)") { DDRD |= (1<<2); THEN("ATMEGA4809 PORTA.DIR = (1<<0)") REQUIRE(porta.DIR.val() == (1<<0)); } + WHEN("ATMEGA328P DDRD |= (1<<3)") { DDRD |= (1<<3); THEN("ATMEGA4809 PORTF.DIR = (1<<5)") REQUIRE(portf.DIR.val() == (1<<5)); } + WHEN("ATMEGA328P DDRD |= (1<<4)") { DDRD |= (1<<4); THEN("ATMEGA4809 PORTC.DIR = (1<<6)") REQUIRE(portc.DIR.val() == (1<<6)); } + WHEN("ATMEGA328P DDRD |= (1<<5)") { DDRD |= (1<<5); THEN("ATMEGA4809 PORTB.DIR = (1<<2)") REQUIRE(portb.DIR.val() == (1<<2)); } + WHEN("ATMEGA328P DDRD |= (1<<6)") { DDRD |= (1<<6); THEN("ATMEGA4809 PORTF.DIR = (1<<4)") REQUIRE(portf.DIR.val() == (1<<4)); } + WHEN("ATMEGA328P DDRD |= (1<<7)") { DDRD |= (1<<7); THEN("ATMEGA4809 PORTA.DIR = (1<<1)") REQUIRE(porta.DIR.val() == (1<<1)); } + } +} + +/*****************************************************************************/ + +SCENARIO("Testing Arduino Nano 4809 PORTD compatibility class", "NANO_compat::PORTDClass") { + PORT_t porta, portb, portc, portf; + + PORTDClass PORTD(&porta, &portb, &portc, &portf); + + REQUIRE(porta.OUT.val() == 0); + REQUIRE(portb.OUT.val() == 0); + REQUIRE(portc.OUT.val() == 0); + REQUIRE(portf.OUT.val() == 0); + + GIVEN("Testing operator = ") { + WHEN("ATMEGA328P PORTD = (1<<0)") { PORTD = (1<<0); THEN("ATMEGA4809 PORTC.OUT = (1<<4)") REQUIRE(portc.OUT.val() == (1<<4)); } + WHEN("ATMEGA328P PORTD = (1<<1)") { PORTD = (1<<1); THEN("ATMEGA4809 PORTC.OUT = (1<<5)") REQUIRE(portc.OUT.val() == (1<<5)); } + WHEN("ATMEGA328P PORTD = (1<<2)") { PORTD = (1<<2); THEN("ATMEGA4809 PORTA.OUT = (1<<0)") REQUIRE(porta.OUT.val() == (1<<0)); } + WHEN("ATMEGA328P PORTD = (1<<3)") { PORTD = (1<<3); THEN("ATMEGA4809 PORTF.OUT = (1<<5)") REQUIRE(portf.OUT.val() == (1<<5)); } + WHEN("ATMEGA328P PORTD = (1<<4)") { PORTD = (1<<4); THEN("ATMEGA4809 PORTC.OUT = (1<<6)") REQUIRE(portc.OUT.val() == (1<<6)); } + WHEN("ATMEGA328P PORTD = (1<<5)") { PORTD = (1<<5); THEN("ATMEGA4809 PORTB.OUT = (1<<2)") REQUIRE(portb.OUT.val() == (1<<2)); } + WHEN("ATMEGA328P PORTD = (1<<6)") { PORTD = (1<<6); THEN("ATMEGA4809 PORTF.OUT = (1<<4)") REQUIRE(portf.OUT.val() == (1<<4)); } + WHEN("ATMEGA328P PORTD = (1<<7)") { PORTD = (1<<7); THEN("ATMEGA4809 PORTA.OUT = (1<<1)") REQUIRE(porta.OUT.val() == (1<<1)); } + } + + GIVEN("Testing operator &= ") { + porta.OUT.set(0xFF); + portb.OUT.set(0xFF); + portc.OUT.set(0xFF); + portf.OUT.set(0xFF); + WHEN("ATMEGA328P PORTD &= ~(1<<0)") { PORTD &= ~(1<<0); THEN("ATMEGA4809 PORTC.OUT = 0xEF") REQUIRE(portc.OUT.val() == 0xEF); } + WHEN("ATMEGA328P PORTD &= ~(1<<1)") { PORTD &= ~(1<<1); THEN("ATMEGA4809 PORTC.OUT = 0xDF") REQUIRE(portc.OUT.val() == 0xDF); } + WHEN("ATMEGA328P PORTD &= ~(1<<2)") { PORTD &= ~(1<<2); THEN("ATMEGA4809 PORTA.OUT = 0xFE") REQUIRE(porta.OUT.val() == 0xFE); } + WHEN("ATMEGA328P PORTD &= ~(1<<3)") { PORTD &= ~(1<<3); THEN("ATMEGA4809 PORTF.OUT = 0xDF") REQUIRE(portf.OUT.val() == 0xDF); } + WHEN("ATMEGA328P PORTD &= ~(1<<4)") { PORTD &= ~(1<<4); THEN("ATMEGA4809 PORTC.OUT = 0xBF") REQUIRE(portc.OUT.val() == 0xBF); } + WHEN("ATMEGA328P PORTD &= ~(1<<5)") { PORTD &= ~(1<<5); THEN("ATMEGA4809 PORTB.OUT = 0xFB") REQUIRE(portb.OUT.val() == 0xFB); } + WHEN("ATMEGA328P PORTD &= ~(1<<6)") { PORTD &= ~(1<<6); THEN("ATMEGA4809 PORTF.OUT = 0xEF") REQUIRE(portf.OUT.val() == 0xEF); } + WHEN("ATMEGA328P PORTD &= ~(1<<7)") { PORTD &= ~(1<<7); THEN("ATMEGA4809 PORTA.OUT = 0xFD") REQUIRE(porta.OUT.val() == 0xFD); } + } + + GIVEN("Testing operator |= ") { + WHEN("ATMEGA328P PORTD |= (1<<0)") { PORTD |= (1<<0); THEN("ATMEGA4809 PORTC.OUT = (1<<4)") REQUIRE(portc.OUT.val() == (1<<4)); } + WHEN("ATMEGA328P PORTD |= (1<<1)") { PORTD |= (1<<1); THEN("ATMEGA4809 PORTC.OUT = (1<<5)") REQUIRE(portc.OUT.val() == (1<<5)); } + WHEN("ATMEGA328P PORTD |= (1<<2)") { PORTD |= (1<<2); THEN("ATMEGA4809 PORTA.OUT = (1<<0)") REQUIRE(porta.OUT.val() == (1<<0)); } + WHEN("ATMEGA328P PORTD |= (1<<3)") { PORTD |= (1<<3); THEN("ATMEGA4809 PORTF.OUT = (1<<5)") REQUIRE(portf.OUT.val() == (1<<5)); } + WHEN("ATMEGA328P PORTD |= (1<<4)") { PORTD |= (1<<4); THEN("ATMEGA4809 PORTC.OUT = (1<<6)") REQUIRE(portc.OUT.val() == (1<<6)); } + WHEN("ATMEGA328P PORTD |= (1<<5)") { PORTD |= (1<<5); THEN("ATMEGA4809 PORTB.OUT = (1<<2)") REQUIRE(portb.OUT.val() == (1<<2)); } + WHEN("ATMEGA328P PORTD |= (1<<6)") { PORTD |= (1<<6); THEN("ATMEGA4809 PORTF.OUT = (1<<4)") REQUIRE(portf.OUT.val() == (1<<4)); } + WHEN("ATMEGA328P PORTD |= (1<<7)") { PORTD |= (1<<7); THEN("ATMEGA4809 PORTA.OUT = (1<<1)") REQUIRE(porta.OUT.val() == (1<<1)); } + } +} \ No newline at end of file diff --git a/drivers/atmelinf.cat b/drivers/atmelinf.cat index 06df75ea..7fa54c00 100644 Binary files a/drivers/atmelinf.cat and b/drivers/atmelinf.cat differ diff --git a/drivers/mEDBG_Virtual_Com_Port.inf b/drivers/mEDBG_Virtual_Com_Port.inf index f69bf51d..7fff7f14 100644 --- a/drivers/mEDBG_Virtual_Com_Port.inf +++ b/drivers/mEDBG_Virtual_Com_Port.inf @@ -19,9 +19,11 @@ DriverVer=06/14/2013,6.0.0.1 [ATMEL] %ATMEL_CDC%=Reader, USB\VID_03EB&PID_2145&MI_01 +%ARDUINO_NANO_EVERY_CDC%=Reader, USB\VID_2341&PID_0058 [ATMEL.NTamd64] %ATMEL_CDC%=DriverInstall, USB\VID_03EB&PID_2145&MI_01 +%ARDUINO_NANO_EVERY_CDC%=Reader, USB\VID_2341&PID_0058 ;---------------------------------------------------------- @@ -86,5 +88,6 @@ ServiceBinary=%12%\usbser.sys [Strings] ATMEL = "Atmel Corp." ATMEL_CDC = "mEDBG Virtual COM Port" +ARDUINO_NANO_EVERY_CDC = "Arduino NANO Every" Serial.SvcDesc = "USB Serial emulation driver" diff --git a/extras/pack.release.bash b/extras/pack.release.bash index a2999d50..3321340b 100755 --- a/extras/pack.release.bash +++ b/extras/pack.release.bash @@ -1,6 +1,6 @@ #!/bin/bash -ex -# pack.*.bash - Bash script to help packaging avr core releases. +# pack.*.bash - Bash script to help packaging samd core releases. # Copyright (c) 2015 Arduino LLC. All right reserved. # # This library is free software; you can redistribute it and/or @@ -17,17 +17,40 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# Version check removed because version string passed from jenkins was incorrect +VERSION_FROM_TAG=$1 +CORE_NAME=$2 +echo $VERSION_FROM_TAG +echo $CORE_NAME VERSION=`grep version= platform.txt | sed 's/version=//g'` +echo $VERSION + +if [ $VERSION != $VERSION_FROM_TAG ]; then + exit 0 +fi PWD=`pwd` FOLDERNAME=`basename $PWD` THIS_SCRIPT_NAME=`basename $0` +FILENAME=core-$CORE_NAME-$VERSION.tar.bz2 +echo $FILENAME -rm -f avr-$VERSION.tar.bz2 +rm -f *.tar.bz2 +rm -f *.json cd .. -tar --transform "s|$FOLDERNAME|$FOLDERNAME-$VERSION|g" --exclude=extras/** --exclude=.git* --exclude=.idea -cjf avr-$VERSION.tar.bz2 $FOLDERNAME +tar --exclude=extras/** --exclude=.git* --exclude=.idea -cjhf $FILENAME $FOLDERNAME cd - -mv ../avr-$VERSION.tar.bz2 . +mv ../$FILENAME . + +CHKSUM=`sha256sum $FILENAME | awk '{ print $1 }'` +SIZE=`wc -c $FILENAME | awk '{ print $1 }'` +cat extras/package_index.json.NewTag.template | +# sed "s/%%BUILD_NUMBER%%/${BUILD_NUMBER}/" | +# sed "s/%%CURR_TIME%%/${CURR_TIME_SED}/" | +sed "s/%%VERSION%%/${VERSION}/" | +sed "s/%%FILENAME%%/${FILENAME}/" | +sed "s/%%CHECKSUM%%/${CHKSUM}/" | +sed "s/%%SIZE%%/${SIZE}/" > package_${CORE_NAME}_${VERSION}_index.json \ No newline at end of file diff --git a/extras/package_index.json.NewTag.template b/extras/package_index.json.NewTag.template new file mode 100644 index 00000000..fcc12676 --- /dev/null +++ b/extras/package_index.json.NewTag.template @@ -0,0 +1,54 @@ +{ + "packages": [ + { + "name": "arduino", + "maintainer": "Arduino Betatesting", + "websiteURL": "http://www.arduino.cc/", + "email": "packages@arduino.cc", + "help": { + "online": "http://www.arduino.cc/en/Reference/HomePage" + }, + "platforms": [ + { + "name": "Arduino megaAVR Boards - Pre-release", + "architecture": "megaavr", + "version": "%%VERSION%%", + "category": "Arduino", + "url": "http://downloads.arduino.cc/cores/staging/%%FILENAME%%", + "archiveFileName": "%%FILENAME%%", + "checksum": "SHA-256:%%CHECKSUM%%", + "size": "%%SIZE%%", + "help": { + "online": "https://github.com/arduino/ArduinoCore-megaavr/issues" + }, + "boards": [ + { + "name": "Arduino UNO WiFi Rev2" + }, + { + "name": "Arduino Nano Every" + } + ], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "avr-gcc", + "version": "7.3.0-atmel3.6.1-arduino5" + }, + { + "packager": "arduino", + "name": "avrdude", + "version": "6.3.0-arduino17" + }, + { + "packager": "arduino", + "name": "arduinoOTA", + "version": "1.3.0" + } + ] + } + ], + "tools": [] + } + ] +} diff --git a/firmwares/MuxTO/JICE_io.cpp b/firmwares/MuxTO/JICE_io.cpp new file mode 100644 index 00000000..91a09281 --- /dev/null +++ b/firmwares/MuxTO/JICE_io.cpp @@ -0,0 +1,41 @@ +/* + stk_io.cpp + + Created: 18-11-2017 15:20:29 + Author: JMR_2 +*/ + +// Includes +#include +#include "JICE_io.h" +#include "sys.h" + +namespace { +// *** Baud rate lookup table for UBRR0 register *** +// Indexed by valid values for PARAM_BAUD_RATE_VAL (defined in JTAG2.h) +uint16_t baud_tbl[8] = {baud(2400), baud(4800), baud(9600), baud(19200), baud(38400), baud(57600), baud(115200), baud(14400)}; +} + +// Functions +uint8_t JICE_io::put(char c) { + SERIALCOM.write(c); + return c; +} + +uint8_t JICE_io::get(void) { + //while (!SERIALCOM.available()); + uint8_t c = SERIALCOM.read(); + return c; +} + +void JICE_io::init(void) { + SERIALCOM.begin(115200); +} + +void JICE_io::flush(void) { + SERIALCOM.flush(); +} + +void JICE_io::set_baud(JTAG2::baud_rate rate) { + +} diff --git a/firmwares/MuxTO/JICE_io.h b/firmwares/MuxTO/JICE_io.h new file mode 100644 index 00000000..da009e85 --- /dev/null +++ b/firmwares/MuxTO/JICE_io.h @@ -0,0 +1,27 @@ +/* + stk_io.h + + Created: 18-11-2017 14:55:53 + Author: JMR_2 +*/ + + +#ifndef JICE_IO_H_ +#define JICE_IO_H_ + +#include +#include "JTAG2.h" + +#warning "modify this to match your USB serial port name" +#define SERIALCOM SerialUSB + +namespace JICE_io { +// Function prototypes +uint8_t put(char c); +uint8_t get(void); +void init(void); +void flush(void); +void set_baud(JTAG2::baud_rate rate); +} + +#endif /* JICE_IO_H_ */ diff --git a/firmwares/MuxTO/JTAG2.cpp b/firmwares/MuxTO/JTAG2.cpp new file mode 100644 index 00000000..8536123d --- /dev/null +++ b/firmwares/MuxTO/JTAG2.cpp @@ -0,0 +1,369 @@ +/* + STK500.cpp + + Created: 08-12-2017 19:47:27 + Author: JMR_2 +*/ + +#include "Arduino.h" +#include "JTAG2.h" +#include "JICE_io.h" +#include "NVM.h" +#include "crc16.h" +#include "UPDI_hi_lvl.h" + +// *** Writeable Parameter Values *** +uint8_t JTAG2::PARAM_EMU_MODE_VAL; +JTAG2::baud_rate JTAG2::PARAM_BAUD_RATE_VAL; + +// *** STK500 packet *** +JTAG2::packet_t JTAG2::packet; + +namespace { +// *** Private variables *** +uint8_t flash_pagesize; +uint8_t eeprom_pagesize; + +// *** Local functions declaration *** +void NVM_fuse_write (uint16_t address, uint8_t data); +void NVM_buffered_write(uint16_t address, uint16_t lenght, uint8_t buff_size, uint8_t write_type); +} + +// *** Packet functions *** +bool JTAG2::receive() { + if (JICE_io::get() != MESSAGE_START) { + return false; + } + uint16_t crc = CRC::next(MESSAGE_START); + for (uint16_t i = 0; i < 6; i++) { + crc = CRC::next(packet.raw[i] = JICE_io::get(), crc); + } + if (packet.size_word[0] > sizeof(packet.body)) { + return false; + } + if (JICE_io::get() != TOKEN) { + return false; + } + crc = CRC::next(TOKEN, crc); + for (uint16_t i = 0; i < packet.size; i++) { + crc = CRC::next(packet.body[i] = JICE_io::get(), crc); + } + uint16_t packet_crc = (JICE_io::get() | ((uint16_t)JICE_io::get() << 8)); + if (packet_crc != crc) { + return false; + } + return true; +} + +void JTAG2::answer() { + uint16_t crc = CRC::next(JICE_io::put(MESSAGE_START)); + for (uint16_t i = 0; i < 6; i++) { + crc = CRC::next(JICE_io::put(packet.raw[i]), crc); + } + crc = CRC::next(JICE_io::put(TOKEN), crc); + for (uint16_t i = 0; i < packet.size_word[0]; i++) { + crc = CRC::next(JICE_io::put(packet.body[i]), crc); + } + JICE_io::put(crc); + JICE_io::put(crc >> 8); +} + +void JTAG2::delay_exec() { + // wait for transmission complete + JICE_io::flush(); + // set baud rate + JICE_io::set_baud(PARAM_BAUD_RATE_VAL); +} + +// *** Set status function *** +void JTAG2::set_status(uint8_t status_code) { + packet.size_word[0] = 1; + packet.body[0] = status_code; +} + +// *** General command functions *** +// *** Signature response message *** +uint8_t JTAG2::sgn_resp[29] { 0x86, 1, + 1, PARAM_FW_VER_M_MIN_VAL, PARAM_FW_VER_M_MAJ_VAL, PARAM_HW_VER_M_VAL, + 1, PARAM_FW_VER_S_MIN_VAL, PARAM_FW_VER_S_MAJ_VAL, PARAM_HW_VER_S_VAL, + 0, 0, 0, 0, 0, 0, + 'J', 'T', 'A', 'G', 'I', 'C', 'E', ' ', 'm', 'k', 'I', 'I', 0 +}; +void JTAG2::sign_on() { + // Initialize JTAGICE2 variables + JTAG2::PARAM_EMU_MODE_VAL = 0x02; + JTAG2::PARAM_BAUD_RATE_VAL = JTAG2::baud_19200; + /* Initialize or enable UPDI */ + UPDI_io::put(UPDI_io::double_break); + UPDI::stcs(UPDI::reg::Control_B, 8); + UPDI::stcs(UPDI::reg::Control_A, 0x80); + // Send sign on message + packet.size_word[0] = sizeof(sgn_resp); + for (uint8_t i = 0; i < sizeof(sgn_resp); i++) { + packet.body[i] = sgn_resp[i]; + } +} + +void JTAG2::get_parameter() { + uint8_t & status = packet.body[0]; + uint8_t & parameter = packet.body[1]; + switch (parameter) { + case PARAM_HW_VER: + packet.size_word[0] = 3; + packet.body[1] = PARAM_HW_VER_M_VAL; + packet.body[2] = PARAM_HW_VER_S_VAL; + break; + case PARAM_FW_VER: + packet.size_word[0] = 5; + packet.body[1] = PARAM_FW_VER_M_MIN_VAL; + packet.body[2] = PARAM_FW_VER_M_MAJ_VAL; + packet.body[3] = PARAM_FW_VER_S_MIN_VAL; + packet.body[4] = PARAM_FW_VER_S_MAJ_VAL; + break; + case PARAM_EMU_MODE: + packet.size_word[0] = 2; + packet.body[1] = PARAM_EMU_MODE_VAL; + break; + case PARAM_BAUD_RATE: + packet.size_word[0] = 2; + packet.body[1] = PARAM_BAUD_RATE_VAL; + break; + case PARAM_VTARGET: + packet.size_word[0] = 3; + packet.body[1] = PARAM_VTARGET_VAL & 0xFF; + packet.body[2] = PARAM_VTARGET_VAL >> 8; + break; + default: + set_status(RSP_ILLEGAL_PARAMETER); + return; + } + status = RSP_PARAMETER; + return; +} + +void JTAG2::set_parameter() { + uint8_t & parameter = packet.body[1]; + switch (parameter) { + case PARAM_EMU_MODE: + PARAM_EMU_MODE_VAL = packet.body[2]; + break; + case PARAM_BAUD_RATE: + PARAM_BAUD_RATE_VAL = (baud_rate)packet.body[2]; + break; + default: + set_status(RSP_ILLEGAL_PARAMETER); + return; + } + set_status(RSP_OK); +} + +void JTAG2::set_device_descriptor() { + flash_pagesize = packet.body[244]; + eeprom_pagesize = packet.body[246]; + set_status(RSP_OK); +} + +// *** Target mode set functions *** +// *** Sets MCU in program mode, if possibe *** +void JTAG2::enter_progmode() { + // Reset the MCU now, to prevent the WDT (if active) to reset it at an unpredictable moment + UPDI::CPU_reset(); + // Now we have time to enter program mode (this mode also disables the WDT) + const uint8_t system_status = UPDI::CPU_mode<0xEF>(); + switch (system_status) { + // in normal operation mode + case 0x82: + // Write NVN unlock key (allows read access to all addressing space) + UPDI::write_key(UPDI::NVM_Prog); + // Request reset + UPDI::CPU_reset(); + // Wait for NVM unlock state + //while (UPDI::CPU_mode() != 0x08); + // already in program mode + case 0x08: + // better clear the page buffer, just in case. + UPDI::sts_b(NVM::NVM_base | NVM::CTRLA, NVM::PBC); + // Turn on LED to indicate program mode + set_status(RSP_OK); + break; + // in other modes fail and inform host of wrong mode + default: + packet.size_word[0] = 2; + packet.body[0] = RSP_ILLEGAL_MCU_STATE; + packet.body[1] = system_status; // 0x01; + } +} + +// *** Sets MCU in normal runnning mode, if possibe *** +void JTAG2::leave_progmode() { + const uint8_t system_status = UPDI::CPU_mode<0xEF>(); + switch (system_status) { + // in program mode + case 0x08: + // Request reset + UPDI::CPU_reset(); + // Wait for normal mode + // while (UPDI::CPU_mode<0xEF>() != 0x82); + // already in normal mode + case 0x82: + // Turn off LED to indicate normal mode + set_status(RSP_OK); + break; + // in other modes fail and inform host of wrong mode + default: + packet.size_word[0] = 2; + packet.body[0] = RSP_ILLEGAL_MCU_STATE; + packet.body[1] = system_status; // 0x01; + } +} + +// *** Read/Write/Erase functions *** + +void JTAG2::read_mem() { + if (UPDI::CPU_mode() != 0x08) { + // fail if not in program mode + packet.size_word[0] = 2; + packet.body[0] = RSP_ILLEGAL_MCU_STATE; + packet.body[1] = 0x01; + } + else { + // in program mode + const uint16_t NumBytes = (packet.body[3] << 8) | packet.body[2]; + // Get physical address for reading + const uint16_t address = (packet.body[7] << 8) | packet.body[6]; + // Set UPDI pointer to address + UPDI::stptr_w(address); + // Read block + UPDI::rep(NumBytes - 1); + packet.body[1] = UPDI::ldinc_b(); + for (uint16_t i = 2; i <= NumBytes; i++) { + packet.body[i] = UPDI_io::get(); + } + packet.size_word[0] = NumBytes + 1; + packet.body[0] = RSP_MEMORY; + } +} + +void JTAG2::write_mem() { + if (UPDI::CPU_mode() != 0x08) { + // fail if not in program mode + packet.size_word[0] = 2; + packet.body[0] = RSP_ILLEGAL_MCU_STATE; + packet.body[1] = 0x01; + } + else { + // in program mode + const uint8_t mem_type = packet.body[1]; + const uint16_t address = packet.body[6] | (packet.body[7] << 8); + const uint16_t lenght = packet.body[2] | (packet.body[3] << 8); /* number of bytes to write */ + const bool is_flash = ((mem_type == MTYPE_FLASH) || (mem_type == MTYPE_BOOT_FLASH)); + const uint8_t buff_size = is_flash ? flash_pagesize : eeprom_pagesize; + const uint8_t write_cmnd = is_flash ? NVM::WP : NVM::ERWP; + switch (mem_type) { + case MTYPE_FUSE_BITS: + case MTYPE_LOCK_BITS: + NVM_fuse_write (address, packet.body[10]); + break; + case MTYPE_FLASH: + case MTYPE_BOOT_FLASH: + case MTYPE_EEPROM_XMEGA: + case MTYPE_USERSIG: + NVM_buffered_write(address, lenght, buff_size, write_cmnd); + break; + default: + set_status(RSP_ILLEGAL_MEMORY_TYPE); + return; + } + set_status(RSP_OK); + } +} + +void JTAG2::erase() { + const uint8_t erase_type = packet.body[1]; + const uint16_t address = packet.body[2] | (packet.body[3] << 8); + switch (erase_type) { + case 0: + // Write Chip Erase key + UPDI::write_key(UPDI::Chip_Erase); + // Request reset + UPDI::CPU_reset(); + // Erase chip process exits program mode, reenter... + enter_progmode(); + break; + case 4: + case 5: + NVM::wait(); + UPDI::sts_b(address, 0xFF); + NVM::command(NVM::ER); + set_status(RSP_OK); + break; + case 6: + case 7: + break; + default: + set_status(RSP_FAILED); + } +} + +// *** Local functions definition *** +namespace { +void NVM_fuse_write (uint16_t address, uint8_t data) { + // Setup UPDI pointer + UPDI::stptr_w(NVM::NVM_base + NVM::DATA_lo); + // Send data to the NVM controller + UPDI::stinc_b(data); + UPDI::stinc_b(0x00); + // Send address to the NVM controller + UPDI::stinc_b(address & 0xFF); + UPDI::stinc_b(address >> 8); + // Execute fuse write + NVM::command(NVM::WFU); +} + +void NVM_buffered_write(const uint16_t address, const uint16_t length, const uint8_t buff_size, const uint8_t write_cmnd) { + uint8_t current_byte_index = 10; /* Index of the first byte to send inside the JTAG2 command body */ + uint16_t bytes_remaining = length; /* number of bytes to write */ + + // Sends a block of bytes from the command body to memory, using the UPDI interface + // On entry, the UPDI pointer must already point to the desired address + // On exit, the UPDI pointer points to the next byte after the last one written + // Returns updated index into the command body, pointing to the first unsent byte. + auto updi_send_block = [] (uint8_t count, uint8_t index) { + count--; + NVM::wait(); + UPDI::rep(count); + UPDI::stinc_b(JTAG2::packet.body[index]); + for (uint8_t i = count; i; i--) { + UPDI_io::put(JTAG2::packet.body[++index]); + UPDI_io::get(); + } + return ++index; + }; + + // Setup UPDI pointer for block transfer + UPDI::stptr_w(address); + /* Check address alignment, calculate number of unaligned bytes to send */ + uint8_t unaligned_bytes = (-address & (buff_size - 1)); + if (unaligned_bytes > bytes_remaining) unaligned_bytes = bytes_remaining; + /* If there are unaligned bytes, they must be sent first */ + if (unaligned_bytes) { + // Send unaligned block + current_byte_index = updi_send_block(unaligned_bytes, current_byte_index); + bytes_remaining -= unaligned_bytes; + NVM::command(write_cmnd); + } + while (bytes_remaining) { + /* Send a buff_size amount of bytes */ + if (bytes_remaining >= buff_size) { + current_byte_index = updi_send_block(buff_size, current_byte_index); + bytes_remaining -= buff_size; + } + /* Send a NumBytes amount of bytes */ + else { + current_byte_index = updi_send_block(bytes_remaining, current_byte_index); + bytes_remaining = 0; + } + NVM::command(write_cmnd); + } +} +} diff --git a/firmwares/MuxTO/JTAG2.h b/firmwares/MuxTO/JTAG2.h new file mode 100644 index 00000000..5a85942f --- /dev/null +++ b/firmwares/MuxTO/JTAG2.h @@ -0,0 +1,157 @@ +/* + packet.h + + Created: 12-11-2017 11:10:31 + Author: JMR_2 +*/ + + +#ifndef JTAG2_H_ +#define JTAG2_H_ + +#include +#include "sys.h" + +namespace JTAG2 { + +// *** Parameter IDs *** +enum parameter { + PARAM_HW_VER = 0x01, + PARAM_FW_VER = 0x02, + PARAM_EMU_MODE = 0x03, + PARAM_BAUD_RATE = 0x05, + PARAM_VTARGET = 0x06 +}; + +// *** valid values for PARAM_BAUD_RATE_VAL +enum baud_rate { + baud_2400 = 0x01, + baud_4800, + baud_9600, + baud_19200, // default + baud_38400, + baud_57600, + baud_115200, + baud_14400 +}; + +// *** Parameter Values *** +constexpr uint8_t PARAM_HW_VER_M_VAL = 0x01; +constexpr uint8_t PARAM_HW_VER_S_VAL = 0x01; +constexpr uint8_t PARAM_FW_VER_M_MIN_VAL = 0x07; +constexpr uint8_t PARAM_FW_VER_M_MAJ_VAL = 0x01; +constexpr uint8_t PARAM_FW_VER_S_MIN_VAL = 0x07; +constexpr uint8_t PARAM_FW_VER_S_MAJ_VAL = 0x06; +extern uint8_t PARAM_EMU_MODE_VAL; +extern baud_rate PARAM_BAUD_RATE_VAL; +constexpr uint16_t PARAM_VTARGET_VAL = 5000; + +// *** General command constants *** +enum cmnd { + CMND_SIGN_OFF = 0x00, + CMND_GET_SIGN_ON = 0x01, + CMND_SET_PARAMETER = 0x02, + CMND_GET_PARAMETER = 0x03, + CMND_WRITE_MEMORY = 0x04, + CMND_READ_MEMORY = 0x05, + CMND_GO = 0x08, + CMND_RESET = 0x0b, + CMND_SET_DEVICE_DESCRIPTOR = 0x0c, + CMND_GET_SYNC = 0x0f, + CMND_ENTER_PROGMODE = 0x14, + CMND_LEAVE_PROGMODE = 0x15, + CMND_XMEGA_ERASE = 0x34 +}; +// *** JTAG Mk2 Single byte status responses *** +enum response { + // Success + RSP_OK = 0x80, + RSP_PARAMETER = 0x81, + RSP_MEMORY = 0x82, + // Error + RSP_FAILED = 0xA0, + RSP_ILLEGAL_PARAMETER = 0xA1, + RSP_ILLEGAL_MEMORY_TYPE = 0xA2, + RSP_ILLEGAL_MEMORY_RANGE = 0xA3, + RSP_ILLEGAL_MCU_STATE = 0xA5, + RSP_ILLEGAL_VALUE = 0xA6, + RSP_ILLEGAL_BREAKPOINT = 0xA8, + RSP_ILLEGAL_JTAG_ID = 0xA9, + RSP_ILLEGAL_COMMAND = 0xAA, + RSP_NO_TARGET_POWER = 0xAB, + RSP_DEBUGWIRE_SYNC_FAILED = 0xAC, + RSP_ILLEGAL_POWER_STATE = 0xAD +}; + +// *** memory types for CMND_{READ,WRITE}_MEMORY *** +enum mem_type { + MTYPE_IO_SHADOW = 0x30, // cached IO registers? + MTYPE_SRAM = 0x20, // target's SRAM or [ext.] IO registers + MTYPE_EEPROM = 0x22, // EEPROM, what way? + MTYPE_EVENT = 0x60, // ICE event memory + MTYPE_SPM = 0xA0, // flash through LPM/SPM + MTYPE_FLASH_PAGE = 0xB0, // flash in programming mode + MTYPE_EEPROM_PAGE = 0xB1, // EEPROM in programming mode + MTYPE_FUSE_BITS = 0xB2, // fuse bits in programming mode + MTYPE_LOCK_BITS = 0xB3, // lock bits in programming mode + MTYPE_SIGN_JTAG = 0xB4, // signature in programming mode + MTYPE_OSCCAL_BYTE = 0xB5, // osccal cells in programming mode + MTYPE_CAN = 0xB6, // CAN mailbox + MTYPE_FLASH = 0xc0, // xmega (app.) flash + MTYPE_BOOT_FLASH = 0xc1, // xmega boot flash + MTYPE_EEPROM_XMEGA = 0xc4, // xmega EEPROM in debug mode + MTYPE_USERSIG = 0xc5, // xmega user signature + MTYPE_PRODSIG = 0xc6 // xmega production signature +}; + +// *** STK500 packet *** +constexpr uint8_t MESSAGE_START = 0x1B; +constexpr int MAX_BODY_SIZE = 450; // Note: should not be reduced to less than 300 bytes. +union __attribute__((packed)) packet_t { + uint8_t raw[6 + MAX_BODY_SIZE]; + struct __attribute__((packed)) { + union { + uint16_t number; + uint8_t number_byte[2]; + }; + union { + uint32_t size; + uint16_t size_word[2]; + uint8_t size_byte[4]; + }; + uint8_t body[MAX_BODY_SIZE]; + }; +} extern packet; +constexpr uint8_t TOKEN = 0x0E; + +// *** Signature response *** +extern uint8_t sgn_resp[29]; +// *** Parameter initialization *** +void init(); + +// *** Packet functions *** +bool receive(); +void answer(); +void delay_exec(); + +// *** Set status function *** +void set_status(uint8_t) __attribute__ ((noinline)); + +// *** General command functions *** +void sign_on(); +void get_parameter(); +void set_parameter(); +void set_device_descriptor(); + +// *** ISP command functions *** +void enter_progmode(); +void leave_progmode(); +void read_signature(); +void read_mem(); +void write_mem(); +void erase(); +} + + + +#endif /* STK_H_ */ diff --git a/firmwares/MuxTO/MuxTO.bin b/firmwares/MuxTO/MuxTO.bin new file mode 100755 index 00000000..b2902861 Binary files /dev/null and b/firmwares/MuxTO/MuxTO.bin differ diff --git a/firmwares/MuxTO/MuxTO.hex b/firmwares/MuxTO/MuxTO.hex new file mode 100644 index 00000000..012215d9 --- /dev/null +++ b/firmwares/MuxTO/MuxTO.hex @@ -0,0 +1,770 @@ +:1010000000100020A91C00008D1C0000A51C000081 +:1010100000000000000000000000000000000000D0 +:101020000000000000000000000000008D1C000017 +:1010300000000000000000008D1C0000191D0000D1 +:101040008D1C00008D1C00008D1C00008D1C0000FC +:101050008D1C00008D1C00008D1C0000911C0000E8 +:101060008D1C0000191C0000091C00008D1C0000D4 +:101070008D1C00008D1C00008D1C00008D1C0000CC +:101080008D1C00008D1C00008D1C000010B5064C4E +:101090002378002B07D1054B002B02D0044800E039 +:1010A00000BF0123237010BD80010020000000005C +:1010B0007C3E0000044B10B5002B03D003490448CC +:1010C00000E000BF10BDC046000000008401002009 +:1010D0007C3E0000E12110B50248490201F080F990 +:1010E00010BDC0465C0400207047000010B504002D +:1010F0000100024801F0F6F8200010BD5C04002059 +:1011000010B5024801F020F9C0B210BD5C04002007 +:1011100010B5024801F0F6F810BDC0465C0400208E +:10112000F8B5451E07000C0000F0B6FC0600EDB255 +:101130000F4800F089FC03231842F9D1300000F079 +:10114000C5FC280000F058FC0A4E3D193319987967 +:1011500000F0CDFCEDB20134E4B2AC4206D033195C +:10116000987900F0E1FC00F0FDFCF4E72000F8BD08 +:1011700002100000A001002070B5FFF7C1FF1B287E +:1011800001D0002070BD224900F0C0FC0026040000 +:10119000FFF7B6FF1F4D2100A85500F0B7FC013640 +:1011A0000400062EF4D1E122EB78A9781B0252004C +:1011B0000B439342E5D8FFF7A3FF0E28E1D12100AE +:1011C00000F0A4FC00260400EA78AB7812021A436F +:1011D0002B791B041A436B791B0613439E420AD2D8 +:1011E000FFF78EFFAB192100987100F08FFC0136DC +:1011F0000400B6B2E8E7FFF783FF0500FFF780FFC2 +:1012000003022B439BB21B1B58425841C0B2B9E7A3 +:10121000FFFF0000A001002070B51B20FFF766FF54 +:10122000184900F073FC00250400174E705DFFF7AD +:101230005DFF210000F06AFC01350400062DF4D1A9 +:101240000E20FFF753FF210000F060FC0025040092 +:10125000F378B2781B021343AB420AD97319987919 +:10126000FFF744FF210000F051FC01350400ADB24E +:10127000EEE7E0B22412FFF739FFE0B2FFF736FFE6 +:1012800070BDC046FFFF0000A001002010B5FFF7B1 +:101290003FFF024B1878FFF727FF10BD9F0100208A +:1012A0000122034B9A7000229871DA707047C04691 +:1012B000A001002010B5022404220E4B01201C7056 +:1012C0000D4B1A7000F05CFC0821032000F0A0FB1D +:1012D0008021200000F09CFB00231D21074A0848C4 +:1012E0009170D370074A195CD15401331D2BFAD188 +:1012F00010BDC0469E0100209F010020A0010020DB +:1013000060000020A601002010B51D4BD8790138DF +:10131000052830D802F0B2FA030B192F2127032237 +:101320009A700022DA700132DA711A720AE005222C +:101330009A7000220121DA700732DA715A72013A8A +:1013400019729A7281229A7118E002229A70002210 +:10135000DA700C4A1278DA71F4E702229A700022ED +:10136000DA70094AF6E703229A700022DA708832AE +:10137000DA71753AD9E7A120FFF792FF10BDC04698 +:10138000A00100209E0100209F01002010B5094B04 +:10139000DA79032A03D0052A08D0A12003E01A7ABB +:1013A000054B80201A70FFF77BFF10BD1A7A034BA4 +:1013B000F7E7C046A00100209E0100209F01002009 +:1013C000074B10B51A00FA321178064AFC33117037 +:1013D0001A78054B80201A70FFF762FF10BDC046D7 +:1013E000A00100209C0100209D0100200B2010B5D1 +:1013F00000F01DFBEF23184008280AD082280AD0ED +:101400000222074B9A700022DA70A5329A71D871C5 +:1014100004E000F0E1FA8020FFF742FF10BDC04673 +:10142000A001002070B50B2000F001FB174C08282C +:1014300008D00223A3700023E370A533A371A43B5B +:10144000E37170BD637A257A1B021D43637B207BA9 +:101450001B022EB21843013E00F038FBF0B200F040 +:10146000CBFA00F029FB0226E071AE4206D800F06C +:1014700079FBA31901369871B6B2F6E70135ABB224 +:101480001B0AE3708223A570A371DAE7A001002094 +:1014900070B50500552000F047FBE02000F044FB4C +:1014A0000824013C2878E4B200F03EFB0135002C12 +:1014B000F7D170BD10B500F08FFA0B2000F0B7FA2D +:1014C000EF231840082806D082280DD10B48FFF7DB +:1014D000DFFF00F081FA80200421400100F0C5FA0E +:1014E0008020FFF7DDFE10BD0222054B9A7000221E +:1014F000DA70A5329A71D871F5E7C04691000020E4 +:10150000A001002070B5164AD379052B05D8032B0E +:101510000FD8002B05D0A0201EE0063B012BFAD8E7 +:1015200070BD1048FFF7B4FF00F056FAFFF7C2FF96 +:10153000F6E70326157A547A0B4800F085FA304214 +:10154000FAD12002FF21284300F08FFA80200221E7 +:10155000400100F08AFA8020FFF7A2FEE0E7C046D3 +:10156000A0010020890000200210000070B50500D5 +:1015700000F092FA040080202900400100F075FA82 +:10158000200000F0A3FA70BDF7B50B2000F04FFA71 +:10159000394B082808D002229A700022DA70A5324E +:1015A0009A71A43ADA71F7BDDA79587B11001D7B84 +:1015B000000240310543C9B22EB2012929D82F4A71 +:1015C000177801220092587A1C7A0002044323B251 +:1015D00028000193764200F079FA781E0640F6B2B0 +:1015E000A64201DD6B461E790A25002E09D029008E +:1015F0003000FFF795FD0500A41BA4B20098FFF78B +:10160000B5FF002C2FD0BEB2BC422ED3290038002B +:10161000EFE71B490F78B32A05D8B12A0ED8A220CC +:10162000FFF73EFEBFE7C12A03D8BF2AF7D903223E +:10163000C8E73C32D2B2012AF1D8F8E71C7C114845 +:1016400000F044FA200000F052FA002000F04FFAB7 +:10165000F0B200F04CFA280A00F049FA8020072185 +:10166000400100F002FA8020DAE72900E0B2FFF73B +:1016700057FD00240500C1E7A00100209C010020C7 +:101680009D0100200610000010B50121002000F08F +:10169000F1FBFFF71FFD00F0ABFA054B05481A8878 +:1016A000054B196801F0FEFFFFF704FE10BDC046B0 +:1016B00086000020BC03002080000020AF4BF7B55F +:1016C0001B78002B00D0FFE0AD4E300001F0E0FEB3 +:1016D000AC4C051E1BD0EFF3108372B6AA4F20004E +:1016E0003B6000F0FDFDA84203DA200000F0F8FDA9 +:1016F00005003B6883F31088A44F2A0039003000AE +:1017000000F0DDFD2A003900200000F001FE20007D +:1017100000F0F2FD051E1BD0EFF3108372B69A4F56 +:1017200030003B6001F0C5FEA84203DA300001F052 +:10173000C0FE05003B6883F31088944F2A003900EF +:10174000200000F04EFE2A003900300000F08AFC34 +:10175000200000F06BFE8E4F3B7883420FD0F022CA +:101760008C4D20002B8893432B8000F05FFE0122DC +:10177000894B38701A70904239D002283CD0200032 +:1017800000F05AFE854F3B78834215D00F22814DE1 +:1017900020002B8893432B8000F04EFE01227E4BCD +:1017A00038701A7090422AD0022828D0002803D11D +:1017B00003232A8813432B80200000F043FE784F38 +:1017C0003B78834223D0734D764A2B882000134008 +:1017D0002B8000F037FE01226F4B387005381A70ED +:1017E000032814D82A8802F049F80E5B5E601023A3 +:1017F0002A8813432B80C2E730232A88F9E72B88F5 +:1018000018432880D8E780235B0013432B8096235E +:10181000654DDB002A689A4215D1200000F018FEC1 +:10182000019000280FD100F085FA604F3B68C01A84 +:10183000C82808D90123514A137000F07BFA5C4B89 +:101840001860019B3B60200000F0EAFD2B68594FB7 +:1018500098422DD0200000F0FBFD3870200000F0F1 +:10186000F7FD00282FD0200000F0DAFD28603000BE +:1018700001F000FE96232968DB00994223D0002462 +:10188000444B30001A8801F00DFF434B59210820CA +:101890001C7000F0BDF82100082000F0B9F812E03B +:1018A00080239B00B1E7C023FBE78023DB00ACE78C +:1018B000394B1B78002BCDD1200000F0C9FD00234F +:1018C000FB569842C6D1F7BD2C4B1B78002BFAD0A3 +:1018D000364C2368002B1CD100F02CFA344B1B68CB +:1018E000C01A354B98421CD8FFF746FC0028EAD0B6 +:1018F000324B987915281CD801F0C0FF2F1F2926DC +:1019000067641D1D381D1D2C3C1D1D381D1D1D1D12 +:101910002C3200F00FFA2368C01AFA235B009842B9 +:10192000DAD90023154A137001221F4B52421A6064 +:10193000C9E734284CD0A02019E0FFF7BBFCFFF723 +:101940006BFCFFF7A3FCBEE7FFF7DEFCF7E7FFF752 +:101950001DFDF4E7FFF7AEFDF1E70422184B1A7006 +:10196000FFF744FD00F0E6F92060E8E78020FFF78C +:1019700097FCE4E7FFF724FDE1E7C046B103002050 +:10198000BC0300205C0400206C03002071030020D5 +:1019900088000020860000207003002068030020DB +:1019A00085000020FFF0FFFF80000020B40300202E +:1019B000B80300208400002060EA0000A00100209D +:1019C0009F010020FFF72EFDB9E7FFF7DDFDB6E729 +:1019D000FFF798FDB3E7C04610B55921082000F085 +:1019E00017F80021082000F013F80E240B2000F057 +:1019F0001EF82042FAD010BD10B50400552000F0AA +:101A000093F8A02000F090F8200000F08DF810BDB1 +:101A100070B5040055200D0000F086F82000403815 +:101A2000C0B200F081F8280000F07EF870BD10B55B +:101A30000400552000F078F820008038C0B200F093 +:101A400073F800F08FF810BD10B50400552000F0B9 +:101A50006BF8042000F068F8E0B200F065F8200AA6 +:101A600000F062F800F07EF810BD70B504000D00C3 +:101A7000552000F059F8442000F056F8E0B200F08C +:101A800053F8200A00F050F800F06CF8280000F03D +:101A90004BF800F067F870BD10B5552000F044F821 +:101AA000292000F041F800F05DF8040000F05AF839 +:101AB0000002204380B210BD10B5552000F034F86C +:101AC000242000F031F800F04DF810BD10B50400EE +:101AD000552000F029F8692000F026F8E0B200F067 +:101AE00023F8200A00F020F800F03CF810BD10B5F3 +:101AF0000400552000F018F8642000F015F82000CC +:101B000000F012F800F02EF810BD0000FF2248404F +:101B1000044BC0B24000C35A09125940C8B29343A3 +:101B200018437047703B000070B50D4D0100040074 +:101B3000280001F04BFD280001F0AFFC00F0FAF89E +:101B40000600280001F0A4FC002804D100F0F2F8FF +:101B5000801B1328F5D9280001F0B0FC200070BDCF +:101B60000004002010B5054C200001F091FC002875 +:101B7000F9D0200001F0A2FCC0B210BD000400208A +:101B8000962170B5174C0500174A2000490001F056 +:101B900089FD002D1BD0012D0DD10021200001F069 +:101BA00015FD200001F079FC0021200001F00EFD60 +:101BB000200001F072FC0F2000F0C2F8200001F0BC +:101BC00067FC051E05D0200001F078FCF6E729002F +:101BD000EBE7E1212000054A890201F063FD2800BE +:101BE00070BDC04600040020130400003104000052 +:101BF000E12110B5024A0348890201F053FD10BDEE +:101C0000310400000004002010B5024801F09EFCE1 +:101C100010BDC046BC03002010B5024801F096FC80 +:101C200010BDC04600040020F7B5002601240F4D6A +:101C30000F49280000F038FA0E4F0F49380000F025 +:101C400033FA0E490E4800F02FFA390023000196AE +:101C5000022200940B4801F04DFC29000196032359 +:101C600004220094084801F045FCF7BD440400201C +:101C70000008004248040020000C0042001000420E +:101C80004C040020BC03002000040020FEE70000FC +:101C9000034B10B51B68002B00D0984710BDC04601 +:101CA000500400200DBEFEE7134A1448110010B581 +:101CB000134B824202D0934212D11300824212D1BE +:101CC0000222104B5A62104B0832DA700F4B20324E +:101CD0009A7300F061F801F04DFDFEE710CB10C1E2 +:101CE0008142FBD3EAE70A4A8B42E9D00021094B43 +:101CF0009A42E5D202C2FBE70000002080010020EA +:101D0000803E0000FC70004100500041004800414E +:101D1000800100204408002010B500F02AF80028B7 +:101D200001D100F01BF810BD014B18607047C04690 +:101D300050040020014B18687047C046540400202E +:101D400070B5041E07D0044D2E6800F011F82B6802 +:101D50009B1BA342F9D370BD5404002010B5034A65 +:101D600013680133136000F08FF910BD5404002094 +:101D70007047002070470000024A53785BB2002B86 +:101D8000FBDB7047000C004010230249CA681A426E +:101D9000FCD0704700080040822210B5154B164C4D +:101DA0005A60A38C023A9343A384FFF7EDFF80228D +:101DB000124B92001B689B0E9B021343A362104BB5 +:101DC000E36292235B00A384FFF7DEFF0223A28C71 +:101DD0001343A384FFF7D8FF80230A4C5B00A36062 +:101DE000FFF7CAFF084B6360FFF7C6FF074B084ABF +:101DF0001A6010BD004000410008004024608000CF +:101E000080BB0A7C000C0040000703009C000020FF +:101E1000006CDC02C0201249124B134A10B54B6013 +:101E2000136A00061B021B0A0343136200240723E4 +:101E30008C600B608021136A09061B021B0A0B438E +:101E400013620A4B196A1A6A1143094A0A431A6251 +:101E500020001A21013400F00DF8072CF8D110BD34 +:101E600010E000E07FBB000000ED00E00004004057 +:101E7000FC05000010B500F053F810BDF7B50C22BA +:101E80004243244D0191535D042B2DD00027AA1805 +:101E90005278214EDB01D418A419204E24789E5983 +:101EA000E4B2BE4203D0013797407E1EB7411B4EBD +:101EB0009E193669B44600266146B14203D0013608 +:101EC0009640711E8E41002F17D10C37784301992F +:101ED0002D18686801290DD1012E06D1410706D4BD +:101EE000D3180D4A9B181C70F7BD0107F8D50421C3 +:101EF0000C43F5E704218C43F2E701209040019A5E +:101F0000012A03D1054A9B189861EDE7034A9B1803 +:101F10005861E9E7703D0000404400410044004141 +:101F20000C225043494B70B5C65C1B185C78DD78B9 +:101F3000042E00D187E0081F172842D801F09EFC2C +:101F40000C313131414141413333393D3D41414112 +:101F50004141413F418341830023002E33D0F001B2 +:101F60003B4E221892191678042232404E1F022E40 +:101F70003BD82D0701D540252A4372B6002901D050 +:101F80001A2935D10123A340324941184B608B6196 +:101F900004239A432E4923185B181A7062B6002056 +:101FA00070BD0123D9E706232B405A1E9341043309 +:101FB000D3E701232B400233CFE70623CDE70723E6 +:101FC000CBE70923C9E7022CC9D10629C7D0224A89 +:101FD00010780028C3D01670204A1078A0431070E3 +:101FE000D079C0090128FBD0B9E702263243C0E707 +:101FF0008D1E012D0CD80123A340164D45196B6091 +:10200000032903D16B6104231A43C3E7AB61FAE7E9 +:10201000012904D1A1400F4BC3189960B8E71049BA +:1020200065082D186D18E10707D50F2629781B01C3 +:1020300031400B432B700123E6E70F262978B1438B +:1020400019432970F7E701204042A9E7703D0000DD +:102050004044004100440041580400200028004250 +:1020600030440041F8B505000E0017000024BC42C2 +:1020700008D02B682800315D1B689847002801D0E4 +:102080000134F4E72000F8BD054A10B51368591C67 +:1020900005D0013B1360002B01D1024B984710BDC6 +:1020A000A000002001000020016070479446F0B5B8 +:1020B00005AC25789C1E621E944162460668920714 +:1020C000376824063A4322430724326000682140DF +:1020D000AD01002442682943022B00D05C030A436F +:1020E00022434260F0BD30B5C0250368AD031C68D3 +:1020F00012052A402243C0240904A40221400A43B5 +:102100001A6003685A6814435C6030BD012302689A +:1021100011680B43136003681A68D207FCD4DA69AC +:10212000D207F9D470470223026811680B43136089 +:102130000268D3699B07FCD4704703681A7ED207F4 +:1021400002D41A7E9207FCD57047002203685A8396 +:1021500070470368187E4007C00F70470368187EF9 +:10216000C0097047802302685B42117E0B431376DF +:1021700070470368187EC007C00F70470368188D4A +:10218000C0B2704703681A7ED207FCD589B201201D +:1021900019857047012203689A7570470122036808 +:1021A0001A7570470368194A30B5934227D0184A08 +:1021B000934227D0174A934220D10B2310210325A5 +:1021C00001229A402B40AB40C224FC359D4012486E +:1021D000A40002600259AA43C0259D402B0013436E +:1021E00080220351D2010D4B0A435A800C4A1168D8 +:1021F0000C4A11605A7852B2002AFBDB30BD092329 +:102200000E21DCE70A230F21D9E7C046000800426F +:10221000000C00420010004200E100E0000C004011 +:102220009C000020A4000020F8B504000F00160058 +:102230001D00FFF7B7FF2000FFF768FF1C21BB0060 +:102240000B4071072268090C0B431360842322683A +:102250009375012F1AD11021012E00D008390C4B93 +:1022600069431868C00001F013FB0722216802408F +:102270008B895203DB04DB0C13438B81226803043C +:102280009089DB0C400B400318439081F8BDC04699 +:10229000A400002010B50400FFF74CFDE0602368A7 +:1022A00020005B699847002808DAFFF743FDE368E0 +:1022B000C01AA3689842F2D30120404210BDF8B57D +:1022C00006000D0017000024BC4207D03000FFF7C5 +:1022D000E1FF002802DB28550134F5E72000F8BDB6 +:1022E0003F20704713B56B460268D97107335468B5 +:1022F00019000122A04716BD10B50221006900F0A7 +:10230000ADFC10BD10B50321006900F0BDFC10BD8F +:1023100010B5130004000A000069032100F0ACFDB1 +:10232000002801D10123636010BD000010B5054CE9 +:1023300003002068421C04D11A68180052699047B3 +:10234000206010BDF0000020074A0300106810B59F +:10235000411C03D001235B42136010BD02211869A8 +:1023600000F07CFDF9E7C046F000002010B50378CE +:102370003A22023303700249024800F029FE10BDE0 +:10238000B40000207804002010B5027800244378BF +:10239000A12A09D1212B05D10E490F489A3A00F004 +:1023A00017FE0134200010BD212AFBD1202B05D1BE +:1023B00008491A3A084800F0ABFCF3E7222B05D095 +:1023C000232BEFD14288054B1A60EBE78278014B53 +:1023D000DA71E7E7AC00002078040020A8000020B4 +:1023E0007047F7B5050017000191FFF7A3FC002423 +:1023F000E860BC4211D2019A022128693B1B1219E4 +:1024000000F0C0FC061E01D0A419F2E7FFF792FC11 +:10241000EB68C01AAB689842F6D32000FEBD0000FE +:10242000014B18687047C046AC000020014B18797A +:10243000C0B27047AC000020014B5879C0B2704761 +:10244000AC000020014B9879C0B27047AC0000206E +:10245000024BD879012318407047C046AC000020D9 +:10246000FA220021054B92009A60054A08321A6050 +:10247000044A59601A6119767047C0465C0400200E +:10248000C43D000078040020704770B50F26CB1DB6 +:1024900002003240D5B22C0000093034092A00DD98 +:1024A00007341C705A1E994200D170BD1300EFE72B +:1024B00010B5040001F082F9200010BD70B582292A +:1024C0001FD1C3B22A4D5A01AA189169294C800123 +:1024D0000C40C021890521439161274908334018E8 +:1024E0005061802228685B01C1180C795242224356 +:1024F0000A7170211A5C8A43110030220A431A546F +:1025000070BD0029FCD1C026C1B219484B01C318C7 +:102510009A68184DB6052A4032439A60083104681B +:1025200048010721154A5A60025D8A4311000122C1 +:102530000A43702102559A692A4032439A610E4A31 +:102540005A61025D8A43110010220A4302559A68BB +:102550000B4920181140802252030A439A609A685E +:10256000920B92039A604023427913434371C7E769 +:1025700030070020FFFFFF8FA40400206405002027 +:10258000FF3F00F070B50400012320CC00212A7821 +:102590002000134380222B70520001F002FAAB7826 +:1025A000DB07FCD46C6270BD1F22144B70B51B6836 +:1025B000590B9C0C9B0111405B0F22401F2900D13D +:1025C0001A391F2A00D1023A072B00D1043B1F25DC +:1025D00000688E01048D0A492A402140314301855B +:1025E000018D14000A00AA4322430285028D1903BB +:1025F000044B13400B43038570BDC046246080002C +:102600003FF8FFFFFF8FFFFFEFF31083036001230D +:10261000436072B670470368002B02D162B6BFF305 +:102620006F8F7047836973B50400002B18D10600C3 +:10263000683635786846FFF7E7FF019BEDB2013B4E +:1026400002D33578EDB2FAE768460193FFF7E3FF6E +:10265000002D01D1002076BD606E236EC01AFAE70E +:102660006846FFF7D1FF22000025019BB432013BF1 +:1026700002D31578EDB2FAE768460193FFF7CBFF76 +:10268000002DE7D02300AC34B03318682368E5E7A9 +:1026900010B543694468DBB25A01A2189168094831 +:1026A00008330840802149030143916091685B0130 +:1026B000890B890391602268D318402219790A4353 +:1026C0001A7110BDFF3F00F0F7B504006846FFF730 +:1026D0009BFF0026019B5A1E0192002B05D0200073 +:1026E000083001F0A6F80600F4E76846FFF793FF0C +:1026F000A5696846002D49D1FFF786FF2000019BA0 +:102700006830013B5A1C0BD00278591E002A05D1B3 +:1027100068460193FFF77FFF2800FEBD0B00F1E73D +:1027200068460193FFF777FF236EB5420ED0626EC5 +:102730009A420BD920005A1CE31822660133D97F34 +:102740000830C9B201F04DF80135EDE7626E9A42EA +:10275000E2D10023236601336846A361FFF754FFEB +:10276000270022000021019B6837013BB532581C2D +:10277000CED0397010785E1E002805D02000117070 +:102780000193FFF785FFEBE73300F0E7FFF73CFF2E +:102790002000019BB430013B5A1C06D00278591E20 +:1027A000D5B2002AB4D00B00F6E7684601930025A5 +:1027B000FFF731FF22002100AC321368B031B5427F +:1027C00002D0086898421DD809689942A4D1002314 +:1027D00068461360A361FFF717FF2700220000215E +:1027E000019BB437013BB532581C00D190E73970DA +:1027F00010785E1E002811D0200011700193FFF7A1 +:1028000047FFEAE72000591CE31811606C33197880 +:102810000830C9B200F0E5FF0135CBE73300E3E74C +:1028200010B50400FFF750FF2000083001F00FF84A +:1028300010BD10B50400FFF747FF2000083000F07E +:10284000F2FF10BDF0B585B0160001910500FFF74D +:102850003BFF02A8FFF7D8FE0024039B5A1E0392F9 +:10286000002B0FD0B442F8D22F000837380000F008 +:10287000DAFF0028F1D0380000F0C4FF019B1855A2 +:102880000134EFE702A8FFF7C6FE200005B0F0BD57 +:10289000F7B542694368D2B21100040008311868E4 +:1028A00049014118C979C90712D552019B189A6884 +:1028B0009204910C002A0CD12000FFF7E9FE636816 +:1028C0001A686369DBB208335B01D3187F22DA71BF +:1028D000F7BDE269002A21D1220001256C32616630 +:1028E0006846E5615A60FFF78FFE26002000210050 +:1028F000019B6836013BB430B5315A1C33D035707A +:1029000002785F1ED2B20A700A78002A04D1200031 +:102910000193FFF7BDFEE8E73B00EEE72200B0328F +:1029200011600022E2612200203268465A60FFF7FF +:102930006BFE0125260020002100019BB436013BDF +:102940006830B5315A1C0ED0357002785F1ED2B295 +:102950000A700A78002A04D120000193FFF798FE3C +:10296000E8E73B00EEE768460193FFF754FEA6E771 +:1029700013B56A4600230C00D01DD371FFF7F6FC97 +:10298000236818186B462060D87916BD03290ED12C +:1029900080220E4B62311B68FF31585C5242024369 +:1029A0005A5468226339FF32FF399954704701291C +:1029B000FCD18022054B24311B68FF31585C524208 +:1029C00002435A5428222339FF32EDE73007002012 +:1029D00070B50024274B0121050020001C70FFF773 +:1029E00049FA01212000FFF749FA234B0121200079 +:1029F0001C70FFF73FFA01212000FFF73FFA202368 +:102A00001E4A0520D1690B43D3610F21FFF788FAD5 +:102A10000F210620FFF784FA194B1A4A5A805A7878 +:102A200052B2002AFBDB1848FFF77EF9174C200052 +:102A3000FFF7A8FD2000FFF7B7FD7F212368C12025 +:102A40001A7880000A401A70042219780A430C216F +:102A50001A701A898A430E491A810A581202120AF8 +:102A60000A5080220A6019787E3A0A431A700123BC +:102A70002B7070BD990400209804002000040040D1 +:102A8000000C0040064000004134000030070020E8 +:102A900000E100E00078002810D00121084B1B68FD +:102AA0001A898A431A810822198B0A431A8304223D +:102AB000198B0A431A830022024B1A607047C046E2 +:102AC00030070020280700200A000E498B699B0B65 +:102AD0009B038B610B680221FF331972987982394D +:102AE00001439971197A8907FCD57F20064B024072 +:102AF000997A8143114380229972997A52420A430A +:102B00009A727047300700200050004100200B7976 +:102B100010B583420AD10800FFF736FC002805D122 +:102B2000034A013093699B0B9B03936110BDC04620 +:102B300030070020F7B50D001100832A19D1EBB240 +:102B4000384C5A01A21891693748AD010840C0219C +:102B5000890501439161354920686D18702108335A +:102B600055615B011A5C8A43110040220A431A54E2 +:102B7000F7BD022A52D12E4BAA00D058002802D00D +:102B800003685B699847B82000F014FE0400294BE5 +:102B9000FF260360234B4360083000F013FE29003A +:102BA00022000023606868323140A3616561E361FF +:102BB000236663664901137053649364443213724D +:102BC000411853728B68184AAD001A40C0239B0508 +:102BD00013438B606269036832401100019207223F +:102BE00008314901CF5C9743BC460327624617432F +:102BF000CF542700019A203752018218576062692A +:102C00002000324052019B1801229B19997A0A43F5 +:102C10009A72FFF73DFD064BEC50A9E72800FFF73D +:102C20004DFCA5E730070020FFFFFF8F6405002063 +:102C30007C040020F43D000070B505000124064B23 +:102C4000A200D258002A05D021002800FFF772FF09 +:102C50000134F4E770BDC046F4000020084B8A0040 +:102C6000D05810B5002803D003689B68984710BD62 +:102C7000044BC9B24901591888688004800CF6E7F2 +:102C80007C0400203007002010B50C00FFF7E6FFA1 +:102C900000280CD0064BE4B2196880230834640184 +:102CA0000C1962795B42134363710223E37110BD17 +:102CB0003007002070B5CBB2124A13485C0189017D +:102CC0008918021951609168104D08330D40802118 +:102CD00089022943402591609168890B890391609D +:102CE00001685A018A1816793543157195796D0670 +:102CF000FCD55B01C918CB79DB07FCD500198068CE +:102D0000C0B270BD6405002030070020FF3F00F016 +:102D100070B5140040220B4B0D001B68FF3359792E +:102D20000A435A710021FFF7C5FFA04200D92000D5 +:102D300000230549A34203D0CA5CEA540133F9E7F2 +:102D400070BDC046300700206405002010B50C4857 +:102D5000C9B24901421893689B049B0C3F2B0BD9C5 +:102D60009468403B9B04A40B9B0CA40323439360F7 +:102D700041188868C0B210BD93689B0B9B03F6E7AF +:102D800030070020F0B51C002F4B85B01B680600F3 +:102D90000D000192002B54D02C4F3B78D8B2002B61 +:102DA00002D10100FFF76AF864233B70284BAA00A8 +:102DB000D058002808D0036822005B680199984722 +:102DC0000400200005B0F0BD29003000FFF746FFE9 +:102DD000844204D929003000FFF740FF0400290095 +:102DE0003000FFF7B3FF0121EBB21A4F03935B01F1 +:102DF0000293029A3B6801989B18FF335A7A0A4360 +:102E00005A72154BA9012200C91800F0C1FD002C0F +:102E1000D7D029003000FFF721FF0028D1D1402171 +:102E2000039B3A6808335B01D31818790143197181 +:102E30000121D971029BD318FF339A7A0A439A72FF +:102E4000BFE701246442BCE7280700209804002063 +:102E50007C040020300700206405002013B56B4679 +:102E6000DC1D22000123FFF78DFF012801D120780E +:102E700016BD01204042FBE7F0B51C003A4B85B07F +:102E80001B680E000192002B3FD08023DB019C4287 +:102E90003BD8364D2B78D8B2002B02D10100FEF77B +:102EA000EDFF642300272B70314BB201D31802933E +:102EB000002C2CD02F4B1B680393FF2333401D00A5 +:102EC00000930835039B6D015D19AB79DB0921D0B7 +:102ED000294B2A49186800F0DBFC294B172158437D +:102EE00000F0D6FC274BEA79920713D49A5D002AAA +:102EF00001D10138F7D201229A55009B5A011D4B8E +:102F00009B189A69920B92039A6101277F423800BD +:102F100005B0F0BD00221B4B25009A553F2C00D96F +:102F20003F252A000199029800F032FD009BA90478 +:102F30005A01104B890C9B18029A7F195A619A69A1 +:102F4000641B920B92030A439A61009B039A083315 +:102F50005B01D3180222DA715979823A0A435A7115 +:102F6000019B5B190193A3E7280700209904002027 +:102F7000A4040020300700209C00002040420F00E5 +:102F8000701101009B04002070B50C001D000D4B5A +:102F9000A601F618110030002A0000F0F9FCE4B296 +:102FA000094A640114196661A369084928000B40A5 +:102FB000A361A169AB04890B9B0C89031943A1612F +:102FC00070BDC046A404002030070020FF3F00F081 +:102FD000F7B5104B0F001B7814000190002B0CD19B +:102FE0000D4B16001D78002D09D00C4E0C4835886D +:102FF00028182D1900F0CCFC35802000FEBD002ED5 +:10300000FBD033007A1900210198FFF7BDFF2D187E +:10301000361AF4E7240600202507002026070020A2 +:1030200025060020F8B500200D00140000AF012A8D +:1030300027D9D31D6A46DB08DB00D31A9D4608005A +:1030400000F072FD6E460323022200210130400091 +:1030500030707370A2420DD2287800280AD0531C19 +:10306000DBB20135B0549C4203D00232F154D2B2EB +:10307000F0E7220031000448FFF7AAFF431E984101 +:10308000C0B2BD46F8BDC04678040020F0B50025AA +:10309000012785B0184B0E0069461F70040000958B +:1030A000FFF766FC154B01A90B806A3BFF3BCB7118 +:1030B0005A330B72009B320009334B800E4B8D71DB +:1030C0004F7108711D70092E0FD00D4E09223770F7 +:1030D0000C4F200000953D80FFF77AFF69462000E5 +:1030E000FFF746FC3A88084935702000FFF770FF6B +:1030F000012005B0F0BDC0462406002009020000F2 +:1031000025070020260700202506002070B5CB7873 +:1031100005000E0092B0022B09D1C9882648FFF79E +:10312000B5FF441EA041C4B2200012B070BD012BF7 +:103130000BD1CA882149112A11D8D2B2002A0ED047 +:103140002800FFF745FF0124EEE70024032BEBD115 +:103150008B78A34205D1CA881949032AEDD90A7888 +:10316000EEE7022B06D18A7916492800FFF75AFFAD +:103170000400D9E7012B02D18A791349F5E7032B23 +:10318000D2D1124B01A91868FFF77FF9104B03A9A0 +:103190001868FFF77AF90F4B05A91868FFF775F95A +:1031A0000D4B07A91868FFF770F96B460533DC77FC +:1031B000B27901A9D9E7C04678040020343E000066 +:1031C000243E0000103E0000283E00000CA08000BD +:1031D00040A0800044A0800048A0800073B5060095 +:1031E00048780D000B2810D800F048FB061B0F3163 +:1031F0000F474C7450536D70097801AA002906D10D +:10320000022311803000FFF7BFFE012076BD0023AE +:103210001380314B1B78012B00D113700223002146 +:10322000F0E78A780023012A09D16A4693802B4B64 +:103230001B78012B00D11371022301AAEFE7264A64 +:103240001370274A93699B0B9B039361DDE78C788E +:10325000012C07D1214B01AA1C70224B1B78137043 +:103260002300DCE7002C08D101201B4B1C4A1870FE +:1032700093699B0B9B039361C8E73000A978FFF724 +:1032800023FCC2E73000FFF741FFBFE70123164AE6 +:10329000C5E70B780020DB06B8D13000FFF7CCFC87 +:1032A0002A200221AA78104BFF301A600C4A1368BA +:1032B0001C5C21434A241954FF34195D2938FF3816 +:1032C00001431955D4E70123084AA8E78A78074B38 +:1032D0001A60B6E7002099E79A040020A2040020B3 +:1032E00030070020E83D0000280700202C070020C0 +:1032F0004B4BF7B51C780700002C00D08FE0494EEF +:1033000033689B8B1B070ED521002000FFF7D6F8F2 +:10331000326810231100FF31887A03438B72424BCD +:103320001C600823938333689A8B52071FD50422AD +:103330009A833E4A1378002B0AD01378013BDBB204 +:1033400013701378D8B2002B02D10121FEF796FD3D +:10335000374A1378002B0AD01378013BDBB2137085 +:103360001378D8B2002B02D10121FEF787FD336814 +:10337000FF331A7AD2061ED510221A7259793032CA +:103380000A435A7160232B4938000A781A423BD10C +:10339000FFF724FF3368FF33002838D08022997963 +:1033A00052420A439A711A7A520604D540221A727E +:1033B000597A0A435A72336807241D8C01239D43AE +:1033C000EDB20193E1B2002D29D0002C27D02B00C3 +:1033D0002341019A134215D02300326808335B0160 +:1033E000D318DA79D20702D4DB799B0707D5124BC1 +:1033F000A200D05800280ED003681B689847019B94 +:10340000A3409D43013CDDE7FFF780FBC2E720219D +:103410000A4A9171C7E73800FFF7B8FAEFE7F7BD3E +:1034200025070020300700202807002099040020ED +:1034300098040020640500207C040020FF50004117 +:1034400010B50248FFF754FF10BDC04678040020B5 +:10345000014B024A1A6070473007002000500041BB +:103460000120704710B50368014A1B6A984710BDD8 +:103470001304000010B504000069FEF747FE2000A9 +:10348000143000F0BBF92000203000F0B7F910BD77 +:1034900010B5143000F0C7F910BD10B504002000BD +:1034A000203000F0C0F90028F9D12069FEF745FE70 +:1034B00010BD10B5203000F0BCF910BD70B506008D +:1034C00004001436300000F09DF9230030331B78DF +:1034D0000500FF2B07D0300000F0ABF90A2802DD11 +:1034E000A36BE26B1A60280070BD10B5143000F0B9 +:1034F000AEF910BDF7B51D0008AB1B7804000093B2 +:1035000009AB1B781600019300234360FA239B004C +:1035100083600D4B0F0008330360143000F052F944 +:103520002000203000F04EF92300009A2C33276150 +:103530009A70019A1E70DA7022005D70FF2330329B +:10354000200013701374FEBD483E000070B50400E7 +:103550000069FEF7FEFD002815D025002069FEF762 +:103560000DFE14350100280000F03BF92300303334 +:103570001B78FF2B07D0280000F05BF9092802DC3C +:10358000636BE26B1A602069FEF7F3FD00280DD033 +:1035900025002035280000F046F9002812D0280028 +:1035A00000F030F9C1B22069FEF7ECFD2069FEF7AA +:1035B000D5FD002805D02069FEF7D4FD2069FEF76F +:1035C000C4FD70BD2069FEF7E9FDEFE7F8B5040022 +:1035D00000690D002600FEF7CCFD2036002834D10E +:1035E0003F27300000F03EF9002825D0EFF310838C +:1035F0000122134211D1EFF305831A003A403B42F6 +:10360000EFD0103A53B2002A10DA0F221340083BD1 +:10361000134A9B089B009B181B682069FEF7A9FDB5 +:103620000028DED02000FFF791FFDAE79B080D4A63 +:10363000C0339B009B58F0E73000290000F0D1F820 +:103640002069FEF7A7FD0120F8BD300000F0EBF87F +:103650000028C5D129002069FEF794FDF3E7C04694 +:103660001CED00E000E100E0F0231B011940802385 +:1036700006209B0099420AD00133FF330130994262 +:1036800005D0802300205B00994200D105307047AF +:103690000F2001400020012903D00239481E81413A +:1036A000481C70470300F0B5040085B003912C332B +:1036B0000E2118781500FEF733FC230026002D3369 +:1036C00018780E212F36FEF72BFC3378022B07D10A +:1036D000230040331878FF2802D00E21FEF720FC8B +:1036E000270030373878FF2814D00121FEF7C2FBBD +:1036F0000C213B781B4A59438B5C521851780122AC +:103700008A401948DB01181860631848E2631B18E7 +:10371000A3631A600122039B11002069FEF784FD58 +:1037200029002000FFF7A0FF290007002000FFF775 +:10373000AFFFF0221540303D6A4255410300390089 +:10374000206901220095FEF7B1FC23002E331A7880 +:1037500031782069FEF7C7FC2069FEF7E4FC05B06C +:10376000F0BDC046703D00001844004114440041C3 +:103770007047000010B5FEF74DFB00F0D1F8FFF7E1 +:10378000F7FF0120FEF7DCFA084C2000FFF720F9D4 +:103790002000FFF77FF9FDF777FFFDF78FFF044B60 +:1037A000002BFAD000E000BFF7E7C0467804002005 +:1037B0000000000010B500F0D7F810BD10B500F003 +:1037C000DDF810BD10B50400802000F0CDF8802297 +:1037D0000021206000F0E5F8002320006360A36072 +:1037E00010BD7F22436810B50133134082689A42AE +:1037F00003D042680468A154436010BD00234360B5 +:1038000083607047826843689A4209D07F21836849 +:103810000268D25C836801330B40836010007047FC +:1038200001204042FBE743688068181A00D58030C9 +:103830007047426883689A4204DB43688068C01A14 +:103840007F30704783684068181A0138F9E782684A +:1038500043689A4203D083680268D05C70470120B5 +:103860004042FBE703007F2240689B680130104024 +:10387000C01A43425841C0B27047000002B47146BA +:1038800049084900095C49008E4402BC7047C046A3 +:10389000002934D00123002210B488422CD3012403 +:1038A0002407A14204D2814202D209011B01F8E798 +:1038B000E400A14204D2814202D249005B00F8E751 +:1038C000884201D3401A1A434C08A04202D3001B7D +:1038D0005C0822438C08A04202D3001B9C082243B0 +:1038E000CC08A04202D3001BDC082243002803D0EE +:1038F0001B0901D00909E3E7101C10BC704701B592 +:10390000002000F00BF802BD0029F8D003B5FFF746 +:10391000C1FF0EBC4243891A1847C0467047C046D3 +:1039200070B500260C4D0D4C641BA410A64209D1A5 +:10393000002600F011F90A4D0A4C641BA410A6429F +:1039400005D170BDB300EB5898470136EEE7B300E0 +:10395000EB5898470136F2E7680100206801002023 +:10396000680100207801002010B5034B01001868A1 +:1039700000F06AF810BDC0460401002010B5034BEA +:103980000100186800F016F810BDC04604010020C0 +:10399000002310B59A4200D110BDCC5CC454013351 +:1039A000F8E703001218934200D1704719700133F1 +:1039B000F9E7000070B50500002910D00C1F23683E +:1039C000002B00DAE418280000F0B5F81D4A13684F +:1039D000002B05D163601460280000F0ADF870BDC5 +:1039E000A34209D9216860188342F3D118685B6843 +:1039F00041182160EEE713005A68002A01D0A24264 +:103A0000F9D919685818A0420BD12068091858181C +:103A100019608242E0D110685268411819605A60FA +:103A2000DAE7A04202D90C232B60D5E721686018A1 +:103A3000824203D1106852684118216062605C6064 +:103A4000CAE7C04634080020F8B50323CD1C9D43C7 +:103A5000083506000C2D1FD20C25A9421ED83000B7 +:103A600000F069F825490A681400002C1AD1244F87 +:103A70003B68002B04D12100300000F043F838608F +:103A80002900300000F03EF8431C2AD10C233000FE +:103A9000336000F051F803E0002DDEDA0C233360D0 +:103AA0000020F8BD23685B1B18D40B2B03D92360BF +:103AB000E418256003E06368A2420DD10B6030007A +:103AC00000F03AF8200007220B30231D9043C21A61 +:103AD000E7D01B1AA350E4E75360F0E722006468C4 +:103AE000C3E70323C41C9C43A042E2D0211A300048 +:103AF00000F008F8431CDCD1C8E7C04634080020B9 +:103B000038080020002370B5064C0500080023602B +:103B100000F014F8431C03D12368002B00D02B6065 +:103B200070BDC046400800200023C25C0133002A5B +:103B3000FBD1581E7047704770470000044A136855 +:103B4000002B03D01818106018007047014BF9E7DC +:103B50003C08002044080020F8B5C046F8BC08BC6A +:103B60009E467047F8B5C046F8BC08BC9E467047F4 +:103B70000000891112239B322446AD573665BF746D +:103B8000488CC19D5AAFD3BE6CCAE5DB7EE9F7F81D +:103B90008110080193331A22A5562C47B7753E644D +:103BA000C99C408DDBBF52AEEDDA64CBFFF976E8FD +:103BB00002218B30100299132667AF763444BD552D +:103BC0004AADC3BC588ED19F6EEBE7FA7CC8F5D9DD +:103BD00083310A2091121803A7772E66B5543C450D +:103BE000CBBD42ACD99E508FEFFB66EAFDD874C9BD +:103BF00004428D5316619F702004A9153227BB36ED +:103C00004CCEC5DF5EEDD7FC6888E1997AABF3BA9C +:103C100085520C4397711E60A1142805B3373A26CC +:103C2000CDDE44CFDFFD56ECE9986089FBBB72AA7C +:103C300006638F7214409D512225AB343006B917AC +:103C40004EEFC7FE5CCCD5DD6AA9E3B8788AF19B5C +:103C500087730E6295501C41A3352A24B11638078C +:103C6000CFFF46EEDDDC54CDEBB962A8F99A708B3C +:103C7000088481951AA793B62CC2A5D33EE1B7F06C +:103C80004008C919522BDB3A644EED5F766DFF7C1C +:103C9000899400859BB712A6ADD224C3BFF136E04C +:103CA000C1184809D33B5A2AE55E6C4FF77D7E6CFC +:103CB0000AA583B4188691972EE3A7F23CC0B5D12C +:103CC0004229CB38500AD91B666FEF7E744CFD5DDC +:103CD0008BB502A499961087AFF326E2BDD034C10C +:103CE000C3394A28D11A580BE77F6E6EF55C7C4DBC +:103CF0000CC685D71EE597F42880A1913AA3B3B2EC +:103D0000444ACD5B5669DF78600CE91D722FFB3E9B +:103D10008DD604C79FF516E4A9902081BBB332A2CB +:103D2000C55A4C4BD7795E68E11C680DF33F7A2E7B +:103D30000EE787F61CC495D52AA1A3B03882B193AB +:103D4000466BCF7A5448DD59622DEB3C700EF91F5B +:103D50008FF706E69DD414C5ABB122A0B99230838B +:103D6000C77B4E6AD5585C49E33D6A2CF11E780F3B +:103D7000000219087F00000080000280000E190870 +:103D80003F40000080061080000F19083F700000BF +:103D900019070180001619081F40000080160680D0 +:103DA000001719080F40000080161280001819082B +:103DB0000F80000080161280001919080F80000083 +:103DC000801612800000000000000000E5220000C4 +:103DD00011230000E122000005230000F922000069 +:103DE000492300002D230000000000000000000017 +:103DF0000000000091280000452800003328000042 +:103E00002128000089240000B1240000252600009C +:103E100041726475696E6F204E616E6F20457665E4 +:103E2000727900000403090441726475696E6F20A1 +:103E30004C4C430012011001020000404123580085 +:103E4000000101020301000000000000000000006A +:103E5000CD35000065200000B33400009B34000025 +:103E600091340000BD340000EB34000065340000E4 +:103E7000A536000075340000613400000000000029 +:103E800010B572B68122114B92000433934213D3C2 +:103E90000F4A01211000147D0C42FCD02021148B0C +:103EA000FF319B085B0021431183D361094B1380D1 +:103EB0000123027D1A42FCD0BFF34F8F064B074A05 +:103EC000DA60BFF34F8FC046FDE7C0460010000028 +:103ED0000040004102A5FFFF00ED00E00400FA05EC +:103EE0008601010701010107060100000000000032 +:103EF0004A544147494345206D6B49490000000041 +:103F000000C20100FF0813040165736172454D563C +:103F10004E20676F72504D564E00000040420F0019 +:103F2000FFFFFFFF40420F00FFFFFFFF00C2010045 +:103F3000000008000904000001020200000524003E +:103F400010010424020605240600010524010101D4 +:103F50000705810310001009040100020A00000097 +:103F60000705020240000007058302400000000030 +:103F7000FFFFFFFF000000008300000002000000C0 +:103F80008200000008010020000000000000000086 +:103F90000000000000000000000000000000000021 +:103FA0000000000000000000000000000000000011 +:103FB0000000000000000000000000000000000001 +:103FC00000000000000000000000000000000000F1 +:103FD00000000000000000000000000000000000E1 +:103FE0000000000000000000B5100000291C0000C7 +:103FF00061240000513400008D100000000000001A +:0400000300001CA934 +:00000001FF diff --git a/firmwares/MuxTO/MuxTO.ino b/firmwares/MuxTO/MuxTO.ino new file mode 100644 index 00000000..85603071 --- /dev/null +++ b/firmwares/MuxTO/MuxTO.ino @@ -0,0 +1,228 @@ +/* + j2updi.cpp + + Created: 11-11-2017 22:29:58 + Author : JMR_2 + Ported to generic Arduino framework by the Arduino Team - 2019 +*/ + +/* + Compile with + fqbn=arduino:samd:muxto:float=default,config=enabled,clock=internal_usb,timer=timer_732Hz,bootloader=4kb,serial=two_uart,usb=cdc + to obtain the binary for Serial-to-USB converter + UPDI programmer on the Arduino Nano Every (MuxTO for brevity) + + Since the "sketch" is (almost) pure Arduino it can be ported easily to other architectures (on chips with two serial ports) + The bitbanged UPDI layer is being reworked to make it compatible with chips with just one UART (eg. atmega 32u4) +*/ + +// Includes +#include "sys.h" +#include "lock.h" +#include "updi_io.h" +#include "JICE_io.h" +#include "JTAG2.h" +#include "UPDI_hi_lvl.h" + +volatile bool updi_mode = false; +unsigned long baudrate = 115200; +unsigned long updi_mode_start = 0; +unsigned long updi_mode_end = 0; +uint8_t stopbits = 1; +uint8_t paritytype = 0; +uint8_t numbits = 8; +int8_t dtr = -1; +int8_t rts = -1; +uint16_t serial_mode = SERIAL_8N1; +bool serialNeedReconfiguration = false; + +char support_buffer[64]; + +struct lock q; + +void setup() { + /* Initialize MCU */ + pinMode(LED_BUILTIN, OUTPUT); + + /* Initialize serial links */ + JICE_io::init(); + UPDI_io::init(); + Serial1.begin(baudrate, serial_mode); + lock_init(&q); + JTAG2::sign_on(); +} + +//#define DEBUG; + +//long blink_timer = 0; +//long blink_delay = 1000; + +void loop() { + +#ifdef DEBUG + if (millis() - blink_timer > blink_delay) { + SYS::toggleLED(); + blink_timer = millis(); + } +#endif + + if (updi_mode == false) { + + //blink_delay = 1000; + + if (int c = Serial1.available()) { + lock(&q); + if (c > Serial.availableForWrite()) { + c = Serial.availableForWrite(); + } + unlock(&q); + Serial1.readBytes(support_buffer, c); + Serial.write(support_buffer, c); + } + + if (int c = Serial.available()) { + lock(&q); + if (c > Serial1.availableForWrite()) { + c = Serial1.availableForWrite(); + } + unlock(&q); + Serial.readBytes(support_buffer, c); + Serial1.write(support_buffer, c); + } + + if (Serial.stopbits() != stopbits) { + serial_mode &= ~HARDSER_STOP_BIT_MASK; + stopbits = Serial.stopbits(); + serialNeedReconfiguration = true; + switch (stopbits) { + case 1: + serial_mode |= HARDSER_STOP_BIT_1; + break; + case 2: + serial_mode |= HARDSER_STOP_BIT_2; + break; + } + } + if (Serial.paritytype() != paritytype) { + serial_mode &= ~HARDSER_PARITY_MASK; + paritytype = Serial.paritytype(); + serialNeedReconfiguration = true; + switch (paritytype) { + case 0: + serial_mode |= HARDSER_PARITY_NONE; + break; + case 1: + serial_mode |= HARDSER_PARITY_EVEN; + break; + case 2: + serial_mode |= HARDSER_PARITY_ODD; + break; + } + } + if (Serial.numbits() != numbits) { + serial_mode &= ~HARDSER_DATA_MASK; + numbits = Serial.numbits(); + serialNeedReconfiguration = true; + switch (numbits) { + case 5: + serial_mode |= HARDSER_DATA_5; + break; + case 6: + serial_mode |= HARDSER_DATA_6; + break; + case 7: + serial_mode |= HARDSER_DATA_7; + break; + case 8: + serial_mode |= HARDSER_DATA_8; + break; + } + } + + if (baudrate == 1200 && Serial.dtr() == 0 && (millis() - updi_mode_end > 200)) { + // don't reenter here if you just finished flashing + updi_mode = true; + updi_mode_start = millis(); + updi_mode_end = 0; + } + + if (Serial.baud() != baudrate || serialNeedReconfiguration || Serial.dtr() != dtr) { + dtr = Serial.dtr(); + if (Serial.dtr() == 1) { + baudrate = Serial.baud(); + Serial1.end(); + if (baudrate != 1200) { + Serial1.begin(baudrate, serial_mode); + serialNeedReconfiguration = false; + // Request reset + UPDI::stcs(UPDI::reg::ASI_Reset_Request, UPDI::RESET_ON); + // Release reset (System remains in reset state until released) + UPDI::stcs(UPDI::reg::ASI_Reset_Request, UPDI::RESET_OFF); + } + } + } + return; + } + + if (updi_mode == true) { + + // updi_mode cannot last more than 1 minute; in that case, break forcibly + if ((updi_mode_end != 0 && (millis() - updi_mode_end) > 500) || ((millis() - updi_mode_start) > 60000)) { + updi_mode = false; + baudrate = -1; + return; + } + + //blink_delay = 100; + + // Receive command + if (!JTAG2::receive()) { + return; + } + // Process command + switch (JTAG2::packet.body[0]) { + case JTAG2::CMND_GET_SIGN_ON: + JTAG2::sign_on(); + break; + case JTAG2::CMND_GET_PARAMETER: + JTAG2::get_parameter(); + break; + case JTAG2::CMND_SET_PARAMETER: + JTAG2::set_parameter(); + break; + case JTAG2::CMND_RESET: + case JTAG2::CMND_ENTER_PROGMODE: + JTAG2::enter_progmode(); + break; + case JTAG2::CMND_SIGN_OFF: + // Restore default baud rate before exiting + JTAG2::PARAM_BAUD_RATE_VAL = JTAG2::baud_19200; + case JTAG2::CMND_LEAVE_PROGMODE: + JTAG2::leave_progmode(); + updi_mode_end = millis(); + break; + case JTAG2::CMND_GET_SYNC: + case JTAG2::CMND_GO: + JTAG2::set_status(JTAG2::RSP_OK); + break; + case JTAG2::CMND_SET_DEVICE_DESCRIPTOR: + JTAG2::set_device_descriptor(); + break; + case JTAG2::CMND_READ_MEMORY: + JTAG2::read_mem(); + break; + case JTAG2::CMND_WRITE_MEMORY: + JTAG2::write_mem(); + break; + case JTAG2::CMND_XMEGA_ERASE: + JTAG2::erase(); + break; + default: + JTAG2::set_status(JTAG2::RSP_FAILED); + break; + } + // send response + JTAG2::answer(); + // some commands need to be executed after sending the answer + JTAG2::delay_exec(); + } +} diff --git a/firmwares/MuxTO/NVM.h b/firmwares/MuxTO/NVM.h new file mode 100644 index 00000000..f2af4ead --- /dev/null +++ b/firmwares/MuxTO/NVM.h @@ -0,0 +1,75 @@ +/* + NVM.h + + Created: 15-12-2017 10:59:53 + Author: JMR_2 +*/ + + +#ifndef NVM_H_ +#define NVM_H_ + +#include +#include "UPDI_lo_lvl.h" + +namespace NVM { +// *** Base Addresses *** +enum base { + NVM_base = 0x1000, /* Base address of the NVM controller */ + Sig_base = 0x1100, /* Base address of the signature */ + Fuse_base = 0x1280, /* Base address of the fuses */ + User_base = 0x1300, /* Base address of the User Row EEPROM */ + EEPROM_base = 0x1400 /* Base address of the main EEPROM */ +}; + +// *** NVM Registers (offsets from NVN_base are enum default values) *** +enum reg { + CTRLA, + CTRLB, + STATUS, + INTCTRL, + INTFLAGS, + Reg_5, + DATA_lo, + DATA_hi, + ADDR_lo, + ADDR_hi +}; + +// *** NVM Commands (write to CTRLA to execute) *** +enum cmnd { + NOP, /* Does nothing */ + WP, /* Write page buffer to memory */ + ER, /* Erase page */ + ERWP, /* Erase and write page */ + PBC, /* Page buffer clear */ + CHER, /* Chip erase: erase Flash and EEPROM */ + EEER, /* EEPROM Erase */ + WFU /* Write fuse */ +}; + +// *** NVM Functions *** +template +void command (uint8_t cmd) { + uint16_t temp; + /* preserve UPDI pointer if requested */ + if (preserve_ptr) temp = UPDI::ldptr_w(); + /* Execute NVM command */ + UPDI::sts_b(NVM_base + CTRLA, cmd); + /* restore UPDI pointer if requested */ + if (preserve_ptr) UPDI::stptr_w(temp); +} + +template +void wait () { + uint16_t temp; + /* preserve UPDI pointer if requested */ + if (preserve_ptr) temp = UPDI::ldptr_w(); + /* Wait while NVM is busy from previous operations */ + while (UPDI::lds_b(NVM::NVM_base + NVM::STATUS) & 0x03); + /* restore UPDI pointer if requested */ + if (preserve_ptr) UPDI::stptr_w(temp); +} +} + +#endif /* NVM_H_ */ diff --git a/firmwares/MuxTO/UPDI_hi_lvl.cpp b/firmwares/MuxTO/UPDI_hi_lvl.cpp new file mode 100644 index 00000000..6f2c2042 --- /dev/null +++ b/firmwares/MuxTO/UPDI_hi_lvl.cpp @@ -0,0 +1,20 @@ +/* + UPDI_hi_lvl.cpp + + Created: 15-02-2018 23:08:39 + Author: JMR_2 +*/ + +#include "UPDI_hi_lvl.h" + +void UPDI::CPU_reset() { + // Request reset + UPDI::stcs(UPDI::reg::ASI_Reset_Request, UPDI::RESET_ON); + // Release reset (System remains in reset state until released) + UPDI::stcs(UPDI::reg::ASI_Reset_Request, UPDI::RESET_OFF); + + // Wait for the reset process to end. + // Either NVMPROG, UROWPROG or BOOTDONE bit will be set in the ASI_SYS_STATUS UPDI register. + // This indicates reset is complete. + while ( UPDI::CPU_mode<0x0E>() == 0 ); +} diff --git a/firmwares/MuxTO/UPDI_hi_lvl.h b/firmwares/MuxTO/UPDI_hi_lvl.h new file mode 100644 index 00000000..55bdf5b7 --- /dev/null +++ b/firmwares/MuxTO/UPDI_hi_lvl.h @@ -0,0 +1,30 @@ +/* + UPDI_hi_lvl.h + + Created: 15-02-2018 23:12:54 + Author: JMR_2 +*/ + + +#ifndef UPDI_HI_LVL_H_ +#define UPDI_HI_LVL_H_ + +#include "UPDI_lo_lvl.h" + +namespace UPDI { +// Constant Expressions +constexpr uint8_t RESET_ON = 0x59; +constexpr uint8_t RESET_OFF = 0x00; + +// Function prototypes +void CPU_reset(); + +// returns the current CPU mode, optionally a mask can be applied to ignore "don't care" status bits +template +uint8_t CPU_mode() { + uint8_t mode = UPDI::lcds(UPDI::reg::ASI_System_Status); + return mode & mask; +} +} + +#endif /* UPDI_HI_LVL_H_ */ diff --git a/firmwares/MuxTO/UPDI_lo_lvl.cpp b/firmwares/MuxTO/UPDI_lo_lvl.cpp new file mode 100644 index 00000000..6c66f7ab --- /dev/null +++ b/firmwares/MuxTO/UPDI_lo_lvl.cpp @@ -0,0 +1,221 @@ +/* + UPDI_cmd.cpp + + Created: 23-11-2017 22:48:36 + Author: JMR_2 +*/ + +// Includes +#include "UPDI_lo_lvl.h" + +// Special optimization options +// Unused r/w I/O registers residing in the space addressable by in/out instructions +// can be used as temporary registers to save a few bytes/cycles. +// Warning: MCU specific!!! +//#define TEMP0 EEARL +//#define TEMP1 GPIOR0 +//#define TEMP2 GPIOR1 + +// Keys +uint8_t UPDI::Chip_Erase[8] {0x65, 0x73, 0x61, 0x72, 0x45, 0x4D, 0x56, 0x4E}; +uint8_t UPDI::NVM_Prog[8] {0x20, 0x67, 0x6F, 0x72, 0x50, 0x4D, 0x56, 0x4E}; +uint8_t UPDI::UserRow_Write[8] {0x65, 0x74, 0x26, 0x73, 0x55, 0x4D, 0x56, 0x4E}; + +// Functions +void UPDI::rep(uint8_t repeats) { +# ifdef TEMP0 + TEMP0 = repeats; +# endif + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0xA0); +# ifdef TEMP0 + UPDI_io::put(TEMP0); +# else + UPDI_io::put(repeats); +# endif +} + +void UPDI::stcs(reg r, uint8_t data) { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0xC0 + r); + UPDI_io::put(data); +} + +uint8_t UPDI::lcds(reg r) { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x80 + r); + return UPDI_io::get(); +} + +void UPDI::read_sib(uint8_t (& buffer)[16]) { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0xE5); + for (uint8_t i = 0; i < 16; i++) { + buffer[i] = UPDI_io::get(); + } +} + +uint8_t UPDI::lds_b(uint16_t address) { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x04); + UPDI_io::put(address & 0xFF); + UPDI_io::put(address >> 8); + return UPDI_io::get(); +} + +uint16_t UPDI::lds_w(uint16_t address) { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x05); + UPDI_io::put(address & 0xFF); + UPDI_io::put(address >> 8); + return UPDI_io::get() | (UPDI_io::get() << 8); +} + +void UPDI::sts_b(uint16_t address, uint8_t data) { +# ifdef TEMP0 + TEMP0 = data; +# endif +# ifdef TEMP1 + TEMP1 = address & 0xFF; +# endif +# ifdef TEMP2 + TEMP2 = address >> 8; +# endif + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x44); +# ifdef TEMP1 + UPDI_io::put(TEMP1); +# else + UPDI_io::put(address & 0xFF); +# endif +# ifdef TEMP2 + UPDI_io::put(TEMP2); +# else + UPDI_io::put(address >> 8); +# endif + UPDI_io::get(); +# ifdef TEMP0 + UPDI_io::put(TEMP0); +# else + UPDI_io::put(data); +# endif + UPDI_io::get(); +} + +void UPDI::sts_w(uint16_t address, uint16_t data) { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x45); + UPDI_io::put(address & 0xFF); + UPDI_io::put(address >> 8); + UPDI_io::get(); + UPDI_io::put(data & 0xFF); + UPDI_io::put(data >> 8); + UPDI_io::get(); +} + +uint8_t UPDI::ldptr_b() { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x28); + return UPDI_io::get(); +} + +uint16_t UPDI::ldptr_w() { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x29); +# ifdef TEMP0 + TEMP0 = UPDI_io::get(); + return (UPDI_io::get() << 8) | TEMP0; +# else + return UPDI_io::get() | (UPDI_io::get() << 8); +# endif +} + +uint8_t UPDI::ld_b() { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x20); + return UPDI_io::get(); +} + +uint16_t UPDI::ld_w() { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x21); + return UPDI_io::get() | (UPDI_io::get() << 8); +} + +uint8_t UPDI::ldinc_b() { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x24); + return UPDI_io::get(); +} + +uint16_t UPDI::ldinc_w() { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x25); + return UPDI_io::get() | (UPDI_io::get() << 8); +} + +void UPDI::stptr_b(uint8_t address) { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x68); + UPDI_io::put(address); + UPDI_io::get(); +} + +void UPDI::stptr_w(uint16_t address) { +# ifdef TEMP0 + TEMP0 = address & 0xFF; +# endif +# ifdef TEMP1 + TEMP1 = address >> 8; +# endif + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x69); +# ifdef TEMP0 + UPDI_io::put(TEMP0); +# else + UPDI_io::put(address & 0xFF); +# endif +# ifdef TEMP1 + UPDI_io::put(TEMP1); +# else + UPDI_io::put(address >> 8); +# endif + UPDI_io::get(); +} + +void UPDI::st_b(uint8_t data) { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x60); + UPDI_io::put(data); + UPDI_io::get(); +} + +void UPDI::st_w(uint16_t data) { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x61); + UPDI_io::put(data & 0xFF); + UPDI_io::put(data >> 8); + UPDI_io::get(); +} + +void UPDI::stinc_b(uint8_t data) { +# ifdef TEMP0 + TEMP0 = data; +# endif + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x64); +# ifdef TEMP0 + UPDI_io::put(TEMP0); +# else + UPDI_io::put(data); +# endif + UPDI_io::get(); +} + +void UPDI::stinc_w(uint16_t data) { + UPDI_io::put(UPDI::SYNCH); + UPDI_io::put(0x65); + UPDI_io::put(data & 0xFF); + UPDI_io::put(data >> 8); + UPDI_io::get(); +} diff --git a/firmwares/MuxTO/UPDI_lo_lvl.h b/firmwares/MuxTO/UPDI_lo_lvl.h new file mode 100644 index 00000000..7915062a --- /dev/null +++ b/firmwares/MuxTO/UPDI_lo_lvl.h @@ -0,0 +1,80 @@ +/* + UPDI_cmd.h + + Created: 23-11-2017 22:46:11 + Author: JMR_2 +*/ + + +#ifndef UPDI_LO_LVL_H_ +#define UPDI_LO_LVL_H_ + +#include +#include "sys.h" +#include "updi_io.h" + +namespace UPDI { +// UPDI registers +enum reg { + Status_A, Status_B, Control_A, Control_B, + Reg_4, Reg_5, Reg_6, ASI_Key_Status, + ASI_Reset_Request, ASI_Control_A, ASI_System_Control_A, ASI_System_Status, + ASI_CRC_Status, Reg_13, Reg_14, Reg_15 +}; + + +// Constant Expressions +constexpr uint8_t SYNCH = 0x55; +constexpr uint8_t ACK = 0x40; + +// Activation Keys +extern uint8_t Chip_Erase[8]; +extern uint8_t NVM_Prog[8]; +extern uint8_t UserRow_Write[8]; + + +// Function prototypes +void rep(uint8_t); + +void stcs(reg, uint8_t); +uint8_t lcds(reg); + +void read_sib(uint8_t (&)[16]); + +uint8_t lds_b(uint16_t); +uint16_t lds_w(uint16_t); + +void sts_b(uint16_t address, uint8_t data); /* Store data at address */ +void sts_w(uint16_t, uint16_t); + +uint8_t ldptr_b(); +uint16_t ldptr_w(); +uint8_t ld_b(); +uint16_t ld_w(); +uint8_t ldinc_b(); +uint16_t ldinc_w(); + +void stptr_b(uint8_t); +void stptr_w(uint16_t); +void st_b(uint8_t); +void st_w(uint16_t); +void stinc_b(uint8_t); +void stinc_w(uint16_t); + +template +inline void write_key(T (& k)[8]) __attribute__(( optimize("no-tree-loop-optimize") )); + +// Function Templates +template +void write_key(T (& k)[8]) { + UPDI_io::put(SYNCH); + UPDI_io::put(0xE0); + T* key_ptr = k; + for (uint8_t i = 8; i != 0; i--) { + UPDI_io::put(*key_ptr); + key_ptr++; + } +} +} + +#endif /* UPDI_LO_LVL_H_ */ diff --git a/firmwares/MuxTO/crc16.cpp b/firmwares/MuxTO/crc16.cpp new file mode 100644 index 00000000..e0766b86 --- /dev/null +++ b/firmwares/MuxTO/crc16.cpp @@ -0,0 +1,61 @@ +/* + crc16.cpp + + Created: 16-01-2018 23:06:40 + Author: JMR_2 +*/ + +#include "crc16.h" +#include "sys.h" +#include "JICE_io.h" + +namespace { +static const uint16_t crc_table[256] = +{ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; +} + +uint16_t CRC::next(uint8_t newchar, uint16_t previous) { + union { + uint16_t word; + struct { + uint8_t low; + uint8_t high; + } byte; + } crc; + crc.word = previous; + crc.word = crc_table[crc.byte.low ^ newchar]; + crc.byte.low ^= (previous >> 8); + return crc.word; +} diff --git a/firmwares/MuxTO/crc16.h b/firmwares/MuxTO/crc16.h new file mode 100644 index 00000000..8942dd47 --- /dev/null +++ b/firmwares/MuxTO/crc16.h @@ -0,0 +1,18 @@ +/* + crc16.h + + Created: 16-01-2018 23:07:05 + Author: JMR_2 +*/ + + +#ifndef CRC16_H_ +#define CRC16_H_ + +#include + +namespace CRC { +uint16_t next (uint8_t newchar, uint16_t previous = 0xFFFF); // 'previous' defaults to CRC seed value, 0xFFFF +} + +#endif /* CRC16_H_ */ diff --git a/firmwares/MuxTO/lock.h b/firmwares/MuxTO/lock.h new file mode 100644 index 00000000..16c5cb36 --- /dev/null +++ b/firmwares/MuxTO/lock.h @@ -0,0 +1,38 @@ +#ifndef _ARCH_LOCK_H_ +#define _ARCH_LOCK_H_ + +struct lock { + uint32_t primask; +}; + +#define LOCK_INIT (struct lock) {} + +static inline void lock_init(struct lock *lock) +{ +} + +// source: +// http://embeddedfreak.wordpress.com/2009/08/14/cortex-m3-global-interruptexception-control/ +// +// confirm this is correct by printing primask before after + +// note arm architecture v7m rm... setting PRIMASK raises priority to 0 + +static inline void lock(struct lock *lock) +{ + uint32_t tmp; + asm volatile ( + "mrs %0, PRIMASK\n\t" + "cpsid i\n\t" + : "=r" (tmp) ); + lock->primask = tmp; +} + +static inline void unlock(struct lock *lock) +{ + asm volatile ( + "msr PRIMASK, %0\n\t" + : : "r" (lock->primask) ); +} + +#endif diff --git a/firmwares/MuxTO/sys.cpp b/firmwares/MuxTO/sys.cpp new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/firmwares/MuxTO/sys.cpp @@ -0,0 +1 @@ + diff --git a/firmwares/MuxTO/sys.h b/firmwares/MuxTO/sys.h new file mode 100644 index 00000000..f9db76f5 --- /dev/null +++ b/firmwares/MuxTO/sys.h @@ -0,0 +1,42 @@ +/* + sys.h + + Created: 02-10-2018 13:07:18 + Author: JMR_2 +*/ + +#ifndef SYS_H_ +#define SYS_H_ + +#define FLASH const + +template +class flash { + private: + const T data; + + public: + // normal constructor + constexpr flash (T _data) : data(_data) {} + // default constructor + constexpr flash () : data(0) {} + + operator T() const { + switch (sizeof(T)) { + case 1: return pgm_read_byte(&data); + case 2: return pgm_read_word(&data); + case 4: return pgm_read_dword(&data); + } + } +}; + +#ifndef F_CPU +#define F_CPU 16000000U +#endif + +constexpr unsigned int baud(unsigned long b) { + return F_CPU / (b * 8.0) - 0.5; +} + + +#endif /* SYS_H_ */ diff --git a/firmwares/MuxTO/updi_io.cpp b/firmwares/MuxTO/updi_io.cpp new file mode 100644 index 00000000..582bd488 --- /dev/null +++ b/firmwares/MuxTO/updi_io.cpp @@ -0,0 +1,68 @@ +/* + updi_io.cpp + + Created: 18-11-2017 10:36:54 + Author: JMR_2 +*/ + +// Includes +#include +#include +#include "updi_io.h" + +// Functions +/* Sends regular characters through the UPDI link */ +uint8_t UPDI_io::put(char c) { + Serial2.write(c); + Serial2.flush(); + //delayMicroseconds(10); + long start = millis(); + while (!Serial2.available() && millis() - start < 20) {} + char d = Serial2.read(); + if (c != d) { + // Serial.println("echo failed! " + String(d, HEX)); + } + return c; +} + +/* Sends special sequences through the UPDI link */ +uint8_t UPDI_io::put(ctrl c) +{ + Serial2.begin(300, SERIAL_8N1); + + switch (c) { + case double_break: + Serial2.write((uint8_t)0x00); + Serial2.flush(); + Serial2.write((uint8_t)0x00); + Serial2.flush(); + break; + case single_break: + Serial2.write((uint8_t)0x00); + Serial2.flush(); + break; + default: + break; + } + delay(15); + while (Serial2.available()) { + Serial2.read(); + } + Serial2.begin(230400, SERIAL_8E2); + return 0; +} + +uint8_t UPDI_io::get() { + uint8_t c; + while (!Serial2.available()) {} + c = Serial2.read(); + //delayMicroseconds(5); + //Serial.println("get! " + String(c, HEX)); + return c; +} + +void UPDI_io::init(void) +{ + Serial2.begin(230400, SERIAL_8E2); + //Serial.begin(115200); +} diff --git a/firmwares/MuxTO/updi_io.h b/firmwares/MuxTO/updi_io.h new file mode 100644 index 00000000..5272d04d --- /dev/null +++ b/firmwares/MuxTO/updi_io.h @@ -0,0 +1,23 @@ +/* + updi_io.h + + Created: 18-11-2017 10:38:31 + Author: JMR_2 +*/ + + +#ifndef UPDI_IO_H_ +#define UPDI_IO_H_ + +namespace UPDI_io { +// Enums +enum ctrl {single_break, double_break, enable}; + +// Function prototypes +uint8_t put(char) __attribute__((optimize("no-tree-loop-optimize"))); +uint8_t put(ctrl); +uint8_t get() __attribute__((optimize("no-tree-loop-optimize"))); +void init(void); +} + +#endif /* UPDI_IO_H_ */ diff --git a/firmwares/MuxTO/updi_io_soft.cpp b/firmwares/MuxTO/updi_io_soft.cpp new file mode 100644 index 00000000..d8ebfe70 --- /dev/null +++ b/firmwares/MuxTO/updi_io_soft.cpp @@ -0,0 +1,250 @@ +/* + updi_io_soft.cpp + + Created: 11-08-2018 22:08:14 + Author: Cristian Balint +*/ + +#ifdef __AVR_ATmega16__ + +// Includes +#include +#include + +#include "sys.h" +#include "updi_io.h" + +// Defines +#ifndef F_CPU +# define F_CPU 16000000U +#endif +#ifndef UPDI_BAUD +# define UPDI_BAUD 225000U // (max 225000 min approx. F_CPU/100) +#endif + +// Cycle timing +#define TXDELAY (uint8_t)(((F_CPU/UPDI_BAUD) - 9) / 3) +#define RXDELAY (uint8_t)(((F_CPU/UPDI_BAUD) - 9) / 3) +#define RXHALFD (uint8_t) (RXDELAY / 2) + +// Check +#if ( (((F_CPU/UPDI_BAUD) - 9) / 3) > 254 ) +# error Low baud rates are not supported - use higher UPDI_BAUD +#endif + +// Functions +/* Sends regular characters through the UPDI link */ +uint8_t UPDI_io::get() { + + // rx input + DDR(UPDI_PORT) &= ~(1 << UPDI_PIN); + // no pullup + PORT(UPDI_PORT) &= ~(1 << UPDI_PIN); + + uint8_t c; + + __asm volatile + ( + " ldi %0, 0 \n\t" // init + " ldi r20, 8 \n\t" // 8 bits + " ldi r19, 3 \n\t" // 1 parity + 2 stop bits + + // wait for start edge + "WaitStart: \n\t" + " sbic %[uart_port], %[uart_pin] \n\t" + " rjmp WaitStart \n\t" + + // skew into middle of edge + " ldi r18, %[rxhalfd] \n\t" // 0.5 bit cycle delay + "HBitDelay: \n\t" + " dec r18 \n\t" + " brne HBitDelay \n\t" + + // 8 bits + "RxBLoop: \n\t" + " ldi r18, %[rxdelay] \n\t" // 1 bit cycle delay + "RxBDelay: \n\t" + " dec r18 \n\t" + " brne RxBDelay \n\t" + " in r21, %[uart_port] \n\t" // get current bit from serial link + " bst r21, %[uart_pin] \n\t" // use T flag + " bld r22, 0\n\t" // to move current data bit + " lsr r22 \n\t" // into carry + " ror %0 \n\t" // accumulate serial data bits into result + " dec r20 \n\t" + " brne RxBLoop \n\t" + " nop \n\t" + + // 1 parity + 2 stop bits + "RxSLoop: \n\t" + " ldi r18, %[rxdelay] \n\t" // 1 bit cycle delay + "RxSDelay: \n\t" + " dec r18 \n\t" + " brne RxSDelay \n\t" + " in r21, %[uart_port] \n\t" // get current bit from serial link + " bst r21, %[uart_pin] \n\t" // use T flag + " bld r22, 0\n\t" // to move current data bit + " lsr r22 \n\t" // into carry + " nop \n\t" // accumulate serial data bits into result + " dec r19 \n\t" + " brne RxSLoop \n\t" + " nop \n\t" + + : "=r" (c) + : [uart_port] "i" (_SFR_IO_ADDR(PIN(UPDI_PORT))), + [uart_pin] "i" (UPDI_PIN), + [rxdelay] "i" (RXDELAY), + [rxhalfd] "i" (RXHALFD) + : "r0", "r18", "r19", "r20", "r21", "r22" + ); + + // re-enable pull up + PORT(UPDI_PORT) |= (1 << UPDI_PIN); + + return c; +} + +uint8_t UPDI_io::put(char c) { + + // tx enable + DDR(UPDI_PORT) |= (1 << UPDI_PIN); + + __asm volatile + ( + " in r0, %[uart_port] \n\t" // port state + " ldi r26, 2 \n\t" // 2 bit stop + " ldi r27, 8 \n\t" // 8 bit parity + " ldi r28, %[txdelay] \n\t" // delay counter + " ldi r30, 8 \n\t" // 8 bit loop + + // pre delay + // 2 bit time + " mov r29, r28 \n\t" + "TxDelay: \n\t" + " nop \n\t" + " dec r29 \n\t" + " brne TxDelay \n\t" + + // start bit + " mov r29, r28 \n\t" + "TxDelayS: \n\t" + " dec r29 \n\t" + " brne TxDelayS \n\t" + " bclr 6 \n\t" + " bld r0, %[uart_pin] \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " out %[uart_port], r0 \n\t" + " nop \n\t" + " nop \n\t" + + // 8 bits + "TxLoop: \n\t" + " mov r29, r28 \n\t" // load delay counter + "TxDelayB: \n\t" // delay (3 cycle * delayCount) - 1 + " dec r29 \n\t" + " brne TxDelayB \n\t" + " bst %[ch], 0 \n\t" // load bit in T + " bld r0, %[uart_pin] \n\t" // store T bit in r0 + " ror %[ch] \n\t" // shift right into carry + " sbci r27, 0 \n\t" // subtract carry (accumulate parity) + " dec r30 \n\t" // decrement bits counter + " out %[uart_port], r0 \n\t" // send bit out + " brne TxLoop \n\t" // loop for each bit + " nop \n\t" + + // parity bit + " mov r29, r28 \n\t" + "TxDelayP: \n\t" + " dec r29 \n\t" + " brne TxDelayP \n\t" + " bst r27, 0 \n\t" // extract accumulated parity + " bld r0, %[uart_pin] \n\t" + " nop \n\t" + " nop \n\t" + " nop \n\t" + " out %[uart_port], r0 \n\t" // send bit out to serial link + " nop \n\t" + " nop \n\t" + + // stop bits + "StopLoop: \n\t" + " mov r29, r28 \n\t" + "TxDelayStop: \n\t" + " dec r29 \n\t" + " brne TxDelayStop \n\t" + " bset 6 \n\t" + " bld r0, %[uart_pin] \n\t" + " nop \n\t" + " nop \n\t" + " dec r26 \n\t" + " out %[uart_port], r0 \n\t" // send bit out to serial link + " brne StopLoop \n\t" // loop for each bit + " nop \n\t" + + : + : [uart_port] "i" (_SFR_IO_ADDR(PORT(UPDI_PORT))), + [uart_pin] "i" (UPDI_PIN), + [txdelay] "i" (TXDELAY), + [ch] "r" (c) + : "r0", "r26", "r27", "r28", "r29", "r30" + ); + + // Ready for RX input + DDR(UPDI_PORT) &= ~(1 << UPDI_PIN); + + return c; +} + +static inline void send_break() { + + // tx enable + DDR(UPDI_PORT) |= (1 << UPDI_PIN); + + // + // 13 cycles = 24.60ms + // + + // low 12 cycle + PORT(UPDI_PORT) &= ~(1 << UPDI_PIN); + _delay_us(2048 * 11); + + // high 1 cycle + PORT(UPDI_PORT) |= (1 << UPDI_PIN); + _delay_us(2048); + + // RX enable + DDR(UPDI_PORT) &= ~(1 << UPDI_PIN); + + return; +} + +/* Sends special sequences through the UPDI link */ +uint8_t UPDI_io::put(ctrl c) { + + switch (c) { + + case double_break: + send_break(); + send_break(); + break; + + case single_break: + send_break(); + break; + + case enable: + + default: + break; + } + + return 0; +} + +void UPDI_io::init(void) { + +} + +#endif //__AVR_ATmega16__ diff --git a/fuses/fuses_4809.bin b/fuses/fuses_4809.bin index d8c8027d..abbe4a09 100644 Binary files a/fuses/fuses_4809.bin and b/fuses/fuses_4809.bin differ diff --git a/libraries/HID/keywords.txt b/libraries/HID/keywords.txt deleted file mode 100644 index 32a9ba5f..00000000 --- a/libraries/HID/keywords.txt +++ /dev/null @@ -1,21 +0,0 @@ -####################################### -# Syntax Coloring Map HID -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -HID KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### -begin KEYWORD2 -SendReport KEYWORD2 -AppendDescriptor KEYWORD2 - -####################################### -# Constants (LITERAL1) -####################################### -HID_TX LITERAL1 \ No newline at end of file diff --git a/libraries/HID/library.properties b/libraries/HID/library.properties deleted file mode 100644 index 499286d4..00000000 --- a/libraries/HID/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=HID -version=1.0 -author=Arduino -maintainer=Arduino -sentence=Module for PluggableUSB infrastructure. Exposes an API for devices like Keyboards, Mice and Gamepads. -paragraph= -category=Communication -url=http://www.arduino.cc/en/Reference/HID -architectures=megaavr diff --git a/libraries/HID/src/HID.cpp b/libraries/HID/src/HID.cpp deleted file mode 100644 index 21ede269..00000000 --- a/libraries/HID/src/HID.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - Copyright (c) 2015, Arduino LLC - Original code (pre-library): Copyright (c) 2011, Peter Barrett - - Permission to use, copy, modify, and/or distribute this software for - any purpose with or without fee is hereby granted, provided that the - above copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR - BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES - OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, - WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - SOFTWARE. - */ - -#include "HID.h" - -#if defined(USBCON) - -HID_& HID() -{ - static HID_ obj; - return obj; -} - -int HID_::getInterface(uint8_t* interfaceCount) -{ - *interfaceCount += 1; // uses 1 - HIDDescriptor hidInterface = { - D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE), - D_HIDREPORT(descriptorSize), - D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) - }; - return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); -} - -int HID_::getDescriptor(USBSetup& setup) -{ - // Check if this is a HID Class Descriptor request - if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; } - if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; } - - // In a HID Class Descriptor wIndex cointains the interface number - if (setup.wIndex != pluggedInterface) { return 0; } - - int total = 0; - HIDSubDescriptor* node; - for (node = rootNode; node; node = node->next) { - int res = USB_SendControl(TRANSFER_PGM, node->data, node->length); - if (res == -1) - return -1; - total += res; - } - - // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol - // due to the USB specs, but Windows and Linux just assumes its in report mode. - protocol = HID_REPORT_PROTOCOL; - - return total; -} - -uint8_t HID_::getShortName(char *name) -{ - name[0] = 'H'; - name[1] = 'I'; - name[2] = 'D'; - name[3] = 'A' + (descriptorSize & 0x0F); - name[4] = 'A' + ((descriptorSize >> 4) & 0x0F); - return 5; -} - -void HID_::AppendDescriptor(HIDSubDescriptor *node) -{ - if (!rootNode) { - rootNode = node; - } else { - HIDSubDescriptor *current = rootNode; - while (current->next) { - current = current->next; - } - current->next = node; - } - descriptorSize += node->length; -} - -int HID_::SendReport(uint8_t id, const void* data, int len) -{ - auto ret = USB_Send(pluggedEndpoint, &id, 1); - if (ret < 0) return ret; - auto ret2 = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, len); - if (ret2 < 0) return ret2; - return ret + ret2; -} - -bool HID_::setup(USBSetup& setup) -{ - if (pluggedInterface != setup.wIndex) { - return false; - } - - uint8_t request = setup.bRequest; - uint8_t requestType = setup.bmRequestType; - - if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) - { - if (request == HID_GET_REPORT) { - // TODO: HID_GetReport(); - return true; - } - if (request == HID_GET_PROTOCOL) { - // TODO: Send8(protocol); - return true; - } - if (request == HID_GET_IDLE) { - // TODO: Send8(idle); - } - } - - if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) - { - if (request == HID_SET_PROTOCOL) { - // The USB Host tells us if we are in boot or report mode. - // This only works with a real boot compatible device. - protocol = setup.wValueL; - return true; - } - if (request == HID_SET_IDLE) { - idle = setup.wValueL; - return true; - } - if (request == HID_SET_REPORT) - { - //uint8_t reportID = setup.wValueL; - //uint16_t length = setup.wLength; - //uint8_t data[length]; - // Make sure to not read more data than USB_EP_SIZE. - // You can read multiple times through a loop. - // The first byte (may!) contain the reportID on a multreport. - //USB_RecvControl(data, length); - } - } - - return false; -} - -HID_::HID_(void) : PluggableUSBModule(1, 1, epType), - rootNode(NULL), descriptorSize(0), - protocol(HID_REPORT_PROTOCOL), idle(1) -{ - epType[0] = EP_TYPE_INTERRUPT_IN; - PluggableUSB().plug(this); -} - -int HID_::begin(void) -{ - return 0; -} - -#endif /* if defined(USBCON) */ diff --git a/libraries/HID/src/HID.h b/libraries/HID/src/HID.h deleted file mode 100644 index ad2b06b1..00000000 --- a/libraries/HID/src/HID.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - Copyright (c) 2015, Arduino LLC - Original code (pre-library): Copyright (c) 2011, Peter Barrett - - Permission to use, copy, modify, and/or distribute this software for - any purpose with or without fee is hereby granted, provided that the - above copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR - BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES - OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, - WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - SOFTWARE. - */ - -#ifndef HID_h -#define HID_h - -#include -#include -#include "api/PluggableUSB.h" - -#if defined(USBCON) - -#define _USING_HID - -// HID 'Driver' -// ------------ -#define HID_GET_REPORT 0x01 -#define HID_GET_IDLE 0x02 -#define HID_GET_PROTOCOL 0x03 -#define HID_SET_REPORT 0x09 -#define HID_SET_IDLE 0x0A -#define HID_SET_PROTOCOL 0x0B - -#define HID_HID_DESCRIPTOR_TYPE 0x21 -#define HID_REPORT_DESCRIPTOR_TYPE 0x22 -#define HID_PHYSICAL_DESCRIPTOR_TYPE 0x23 - -// HID subclass HID1.11 Page 8 4.2 Subclass -#define HID_SUBCLASS_NONE 0 -#define HID_SUBCLASS_BOOT_INTERFACE 1 - -// HID Keyboard/Mouse bios compatible protocols HID1.11 Page 9 4.3 Protocols -#define HID_PROTOCOL_NONE 0 -#define HID_PROTOCOL_KEYBOARD 1 -#define HID_PROTOCOL_MOUSE 2 - -// Normal or bios protocol (Keyboard/Mouse) HID1.11 Page 54 7.2.5 Get_Protocol Request -// "protocol" variable is used for this purpose. -#define HID_BOOT_PROTOCOL 0 -#define HID_REPORT_PROTOCOL 1 - -// HID Request Type HID1.11 Page 51 7.2.1 Get_Report Request -#define HID_REPORT_TYPE_INPUT 1 -#define HID_REPORT_TYPE_OUTPUT 2 -#define HID_REPORT_TYPE_FEATURE 3 - -typedef struct -{ - uint8_t len; // 9 - uint8_t dtype; // 0x21 - uint8_t addr; - uint8_t versionL; // 0x101 - uint8_t versionH; // 0x101 - uint8_t country; - uint8_t desctype; // 0x22 report - uint8_t descLenL; - uint8_t descLenH; -} HIDDescDescriptor; - -typedef struct -{ - InterfaceDescriptor hid; - HIDDescDescriptor desc; - EndpointDescriptor in; -} HIDDescriptor; - -class HIDSubDescriptor { -public: - HIDSubDescriptor *next = NULL; - HIDSubDescriptor(const void *d, const uint16_t l) : data(d), length(l) { } - - const void* data; - const uint16_t length; -}; - -class HID_ : public PluggableUSBModule -{ -public: - HID_(void); - int begin(void); - int SendReport(uint8_t id, const void* data, int len); - void AppendDescriptor(HIDSubDescriptor* node); - -protected: - // Implementation of the PluggableUSBModule - int getInterface(uint8_t* interfaceCount); - int getDescriptor(USBSetup& setup); - bool setup(USBSetup& setup); - uint8_t getShortName(char* name); - -private: - unsigned int epType[1]; - - HIDSubDescriptor* rootNode; - uint16_t descriptorSize; - - uint8_t protocol; - uint8_t idle; -}; - -// Replacement for global singleton. -// This function prevents static-initialization-order-fiasco -// https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use -HID_& HID(); - -#define D_HIDREPORT(length) { 9, 0x21, 0x01, 0x01, 0, 1, 0x22, lowByte(length), highByte(length) } - -#else - -#error "No Native USB support available on this board" - -#endif // USBCON - -#endif // HID_h diff --git a/libraries/SPI/examples/DigitalPotControl/DigitalPotControl.ino b/libraries/SPI/examples/DigitalPotControl/DigitalPotControl.ino index c7afcc0a..8719058e 100644 --- a/libraries/SPI/examples/DigitalPotControl/DigitalPotControl.ino +++ b/libraries/SPI/examples/DigitalPotControl/DigitalPotControl.ino @@ -63,9 +63,11 @@ void loop() { void digitalPotWrite(int address, int value) { // take the SS pin low to select the chip: digitalWrite(slaveSelectPin, LOW); + delay(100); // send in the address and value via SPI: SPI.transfer(address); SPI.transfer(value); + delay(100); // take the SS pin high to de-select the chip: digitalWrite(slaveSelectPin, HIGH); } diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index 3f04fe4e..d2c94f1c 100644 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -24,8 +24,6 @@ #define SPI_IMODE_EXTINT 1 #define SPI_IMODE_GLOBAL 2 -const SPISettings DEFAULT_SPI_SETTINGS = SPISettings(); - SPIClass::SPIClass(uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint8_t uc_pinMOSI, uint8_t uc_pinSS, uint8_t uc_mux) { initialized = false; @@ -44,12 +42,11 @@ void SPIClass::begin() PORTMUX.TWISPIROUTEA |= _uc_mux; - // We don't need HW SS since salve/master mode is selected via registers, so make it simply INPUT - pinMode(_uc_pinSS, INPUT); pinMode(_uc_pinMosi, OUTPUT); pinMode(_uc_pinSCK, OUTPUT); // MISO is set to input by the controller + // We don't need HW SS since salve/master mode is selected via registers SPI0.CTRLB |= (SPI_SSD_bm); SPI0.CTRLA |= (SPI_ENABLE_bm | SPI_MASTER_bm); @@ -67,7 +64,7 @@ void SPIClass::init() initialized = true; } -void SPIClass::config(SPISettings settings) +void SPIClass::config(SPISettingsMegaAVR settings) { SPI0.CTRLA = settings.ctrla; SPI0.CTRLB = settings.ctrlb; @@ -131,7 +128,7 @@ void SPIClass::detachMaskedInterrupts() { uint8_t shift = 0; while (temp != 0) { if (temp & 1) { - uint8_t* pin_ctrl_reg = getPINnCTRLregister(portToPortStruct(shift/8), shift%8); + volatile uint8_t* pin_ctrl_reg = getPINnCTRLregister(portToPortStruct(shift/8), shift%8); irqMap[shift] = *pin_ctrl_reg; *pin_ctrl_reg &= ~(PORT_ISC_gm); } @@ -142,7 +139,7 @@ void SPIClass::detachMaskedInterrupts() { shift = 32; while (temp != 0) { if (temp & 1) { - uint8_t* pin_ctrl_reg = getPINnCTRLregister(portToPortStruct(shift/8), shift%8); + volatile uint8_t* pin_ctrl_reg = getPINnCTRLregister(portToPortStruct(shift/8), shift%8); irqMap[shift] = *pin_ctrl_reg; *pin_ctrl_reg &= ~(PORT_ISC_gm); } @@ -156,7 +153,7 @@ void SPIClass::reattachMaskedInterrupts() { uint8_t shift = 0; while (temp != 0) { if (temp & 1) { - uint8_t* pin_ctrl_reg = getPINnCTRLregister(portToPortStruct(shift/8), shift%8); + volatile uint8_t* pin_ctrl_reg = getPINnCTRLregister(portToPortStruct(shift/8), shift%8); *pin_ctrl_reg |= irqMap[shift]; } temp = temp >> 1; @@ -166,7 +163,7 @@ void SPIClass::reattachMaskedInterrupts() { shift = 32; while (temp != 0) { if (temp & 1) { - uint8_t* pin_ctrl_reg = getPINnCTRLregister(portToPortStruct(shift/8), shift%8); + volatile uint8_t* pin_ctrl_reg = getPINnCTRLregister(portToPortStruct(shift/8), shift%8); *pin_ctrl_reg |= irqMap[shift]; } temp = temp >> 1; @@ -174,7 +171,7 @@ void SPIClass::reattachMaskedInterrupts() { } } -void SPIClass::beginTransaction(SPISettings settings) +void SPIClass::beginTransaction(SPISettingsMegaAVR settings) { if (interruptMode != SPI_IMODE_NONE) { diff --git a/libraries/SPI/src/SPI.h b/libraries/SPI/src/SPI.h index cd853f92..53e15839 100644 --- a/libraries/SPI/src/SPI.h +++ b/libraries/SPI/src/SPI.h @@ -21,6 +21,7 @@ #define _SPI_H_INCLUDED #include +#include #ifndef USE_MALLOC_FOR_IRQ_MAP #define USE_MALLOC_FOR_IRQ_MAP 0 @@ -31,7 +32,7 @@ // - endTransaction() // - usingInterrupt() // - SPISetting(clock, bitOrder, dataMode) -#define SPI_HAS_TRANSACTION 1 +// #define SPI_HAS_TRANSACTION 1 // SPI_HAS_NOTUSINGINTERRUPT means that SPI has notUsingInterrupt() method #define SPI_HAS_NOTUSINGINTERRUPT 1 @@ -48,11 +49,13 @@ #define SPI_INTERRUPT_DISABLE 0 #define SPI_INTERRUPT_ENABLE 1 +#ifndef EXTERNAL_NUM_INTERRUPTS #define EXTERNAL_NUM_INTERRUPTS NUM_TOTAL_PINS +#endif -class SPISettings { +class SPISettingsMegaAVR : public arduino::SPISettings { public: - SPISettings(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) { + SPISettingsMegaAVR(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) { if (__builtin_constant_p(clock)) { init_AlwaysInline(clock, bitOrder, dataMode); } else { @@ -61,7 +64,9 @@ class SPISettings { } // Default speed set to 4MHz, SPI mode set to MODE 0 and Bit order set to MSB first. - SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); } + SPISettingsMegaAVR() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); } + + SPISettingsMegaAVR(SPISettings& x) { SPISettingsMegaAVR(x.getClockFreq(), x.getBitOrder(), x.getDataMode()); } private: void init_MightInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) { @@ -140,12 +145,12 @@ class SPISettings { /* member variables containing the desired SPI settings */ uint8_t ctrla; uint8_t ctrlb; - friend class SPIClass; + friend class SPIClassMegaAVR; }; -class SPIClass { +class SPIClassMegaAVR : public arduino::HardwareSPI { public: - SPIClass(uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint8_t uc_pinMOSI, uint8_t uc_pinSS, uint8_t uc_mux); + SPIClassMegaAVR(uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint8_t uc_pinMOSI, uint8_t uc_pinSS, uint8_t uc_mux); byte transfer(uint8_t data); uint16_t transfer16(uint16_t data); @@ -154,7 +159,10 @@ class SPIClass { // Transaction Functions void usingInterrupt(int interruptNumber); void notUsingInterrupt(int interruptNumber); - void beginTransaction(SPISettings settings); + void beginTransaction(SPISettingsMegaAVR settings); + void beginTransaction(SPISettings settings) { + beginTransaction(SPISettingsMegaAVR(settings)); + } void endTransaction(void); void begin(); @@ -167,13 +175,16 @@ class SPIClass { private: void init(); - void config(SPISettings settings); + void config(SPISettingsMegaAVR settings); + void config(SPISettings settings) { + config(SPISettingsMegaAVR(settings)); + } // These undocumented functions should not be used. SPI.transfer() // polls the hardware flag which is automatically cleared as the // AVR responds to SPI's interrupt - inline static void attachInterrupt() { SPI0.INTCTRL |= (SPI_IE_bm); } - inline static void detachInterrupt() { SPI0.INTCTRL &= ~(SPI_IE_bm); } + inline void attachInterrupt() { SPI0.INTCTRL |= (SPI_IE_bm); } + inline void detachInterrupt() { SPI0.INTCTRL &= ~(SPI_IE_bm); } void detachMaskedInterrupts(); void reattachMaskedInterrupts(); @@ -197,6 +208,7 @@ class SPIClass { #endif }; +#define SPIClass SPIClassMegaAVR #if SPI_INTERFACES_COUNT > 0 extern SPIClass SPI; diff --git a/libraries/SoftwareSerial/src/SoftwareSerial.h b/libraries/SoftwareSerial/src/SoftwareSerial.h index b1a37c4a..edcf7607 100644 --- a/libraries/SoftwareSerial/src/SoftwareSerial.h +++ b/libraries/SoftwareSerial/src/SoftwareSerial.h @@ -103,7 +103,7 @@ class SoftwareSerial : public Stream virtual int read(); virtual int available(); virtual void flush(); - operator bool() { return true; } + explicit operator bool() { return true; } using Print::write; diff --git a/libraries/Wire/keywords.txt b/libraries/Wire/keywords.txt index 5e3d2b1c..04b8301a 100644 --- a/libraries/Wire/keywords.txt +++ b/libraries/Wire/keywords.txt @@ -6,6 +6,8 @@ # Datatypes (KEYWORD1) ####################################### +Wire KEYWORD2 + ####################################### # Methods and Functions (KEYWORD2) ####################################### @@ -18,12 +20,6 @@ requestFrom KEYWORD2 onReceive KEYWORD2 onRequest KEYWORD2 -####################################### -# Instances (KEYWORD2) -####################################### - -Wire KEYWORD2 - ####################################### # Constants (LITERAL1) ####################################### diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index febee50c..cdec85cf 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -95,7 +95,7 @@ void TwoWire::setClock(uint32_t clock) TWI_MasterSetBaud(clock); } -uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool sendStop) { +size_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool sendStop) { if(quantity > BUFFER_LENGTH){ quantity = BUFFER_LENGTH; } @@ -109,17 +109,17 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool sendStop) { return bytes_read; } -uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity) +size_t TwoWire::requestFrom(uint8_t address, size_t quantity) { return requestFrom(address, quantity, true); } -uint8_t TwoWire::requestFrom(int address, int quantity) +size_t TwoWire::requestFrom(int address, int quantity) { return requestFrom((uint8_t)address, (size_t)quantity, true); } -uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) +size_t TwoWire::requestFrom(int address, int quantity, int sendStop) { return requestFrom((uint8_t)address, (size_t)quantity, (bool)sendStop); } diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 7a1375bd..3871e350 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -23,6 +23,7 @@ #define TwoWire_h #include +#include #define BUFFER_LENGTH 128 @@ -57,10 +58,10 @@ class TwoWire : public HardwareI2C void beginTransmission(int); uint8_t endTransmission(void); uint8_t endTransmission(bool); - uint8_t requestFrom(uint8_t, size_t); - uint8_t requestFrom(uint8_t, size_t, bool); - uint8_t requestFrom(int, int); - uint8_t requestFrom(int, int, int); + size_t requestFrom(uint8_t, size_t); + size_t requestFrom(uint8_t, size_t, bool); + size_t requestFrom(int, int); + size_t requestFrom(int, int, int); virtual size_t write(uint8_t); virtual size_t write(const uint8_t *, size_t); virtual int available(void); diff --git a/libraries/Wire/src/utility/twi.c b/libraries/Wire/src/utility/twi.c index 94098f54..5e34609a 100644 --- a/libraries/Wire/src/utility/twi.c +++ b/libraries/Wire/src/utility/twi.c @@ -65,9 +65,10 @@ void TWI_MasterInit(uint32_t frequency) if(twi_mode != TWI_MODE_UNKNOWN) return; // Enable pullups just in case, should have external ones though - //pinMode(PIN_WIRE_SDA, INPUT_PULLUP); - //pinMode(PIN_WIRE_SCL, INPUT_PULLUP); - +#ifdef NO_EXTERNAL_I2C_PULLUP + pinMode(PIN_WIRE_SDA, INPUT_PULLUP); + pinMode(PIN_WIRE_SCL, INPUT_PULLUP); +#endif PORTMUX.TWISPIROUTEA |= TWI_MUX; twi_mode = TWI_MODE_MASTER; @@ -200,7 +201,7 @@ void TWI_MasterSetBaud(uint32_t frequency){ t_rise = 1000; } - uint32_t baud = ((F_CPU_CORRECTED/frequency) - (((F_CPU_CORRECTED*t_rise)/1000)/1000)/1000 - 10)/2; + uint32_t baud = (F_CPU_CORRECTED / frequency - F_CPU_CORRECTED / 1000 / 1000 * t_rise / 1000 - 10) / 2; TWI0.MBAUD = (uint8_t)baud; } @@ -269,8 +270,11 @@ uint8_t TWI_MasterRead(uint8_t slave_address, * \param bytesToWrite Number of bytes to write. * \param bytesToRead Number of bytes to read. * - * \retval true If transaction could be started. - * \retval false If transaction could not be started. + * \retval 0:success + * \retval 1:data too long to fit in transmit buffer + * \retval 2:received NACK on transmit of address + * \retval 3:received NACK on transmit of data + * \retval 4:other error */ uint8_t TWI_MasterWriteRead(uint8_t slave_address, uint8_t *write_data, @@ -334,8 +338,21 @@ uint8_t TWI_MasterWriteRead(uint8_t slave_address, // return bytes really read ret = master_bytesRead; } else { - // return 0 if success, >0 otherwise - ret = (master_result == TWIM_RESULT_OK ? 0 : 1); + // return 0 if success, >0 otherwise (follow classic AVR conventions) + switch (master_result) { + case TWIM_RESULT_OK: + ret = 0; + break; + case TWIM_RESULT_BUFFER_OVERFLOW: + ret = 1; + break; + case TWIM_RESULT_NACK_RECEIVED: + ret = 3; + break; + default: + ret = 4; + break; + } } return ret; diff --git a/platform.txt b/platform.txt index 25d1987e..50992217 100644 --- a/platform.txt +++ b/platform.txt @@ -3,10 +3,10 @@ # ------------------------------ # # For more info: -# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification +# https://arduino.github.io/arduino-cli/latest/platform-specification/ name=Arduino megaAVR Boards -version=1.6.26 +version=1.8.8 # AVR compile variables # --------------------- @@ -18,10 +18,10 @@ compiler.warning_flags.more=-Wall compiler.warning_flags.all=-Wall -Wextra # Default "compiler.path" is correct, change only if you want to override the initial value -compiler.path={runtime.tools.avr-gcc-5.4.0-atmel3.6.1-arduino2.path}/bin/ +compiler.path={runtime.tools.avr-gcc.path}/bin/ compiler.c.cmd=avr-gcc compiler.c.flags=-c -g -Os {compiler.warning_flags} -std=gnu11 -ffunction-sections -fdata-sections -MMD -flto -fno-fat-lto-objects -compiler.c.elf.flags={compiler.warning_flags} -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections +compiler.c.elf.flags={compiler.warning_flags} -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections -Wl,--section-start={build.text_section_start} compiler.c.elf.cmd=avr-gcc compiler.S.flags=-c -g -x assembler-with-cpp -flto -MMD compiler.cpp.cmd=avr-g++ @@ -31,8 +31,10 @@ compiler.ar.flags=rcs compiler.objcopy.cmd=avr-objcopy compiler.objcopy.eep.flags=-O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 compiler.elf2hex.flags=-O ihex -R .eeprom +compiler.elf2hex.bin.flags=-O binary -R .eeprom compiler.elf2hex.cmd=avr-objcopy compiler.ldflags= +compiler.libraries.ldflags= compiler.size.cmd=avr-size # This can be overridden in boards.txt @@ -65,11 +67,12 @@ archive_file_path={build.path}/{archive_file} recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}" ## Combine gc-sections, archives, and objects -recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mmcu={build.mcu} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" {object_files} "{build.path}/{archive_file}" "-L{build.path}" -lm +recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mmcu={build.mcu} {compiler.c.elf.extra_flags} {compiler.ldflags} -o "{build.path}/{build.project_name}.elf" {object_files} {compiler.libraries.ldflags} "{build.path}/{archive_file}" "-L{build.path}" -lm "-Wl,-Map,{build.path}/{build.project_name}.map" ## Create output files (.eep and .hex) recipe.objcopy.eep.pattern="{compiler.path}{compiler.objcopy.cmd}" {compiler.objcopy.eep.flags} {compiler.objcopy.eep.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.eep" recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex" +recipe.objcopy.bin.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.bin.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin" ## Save hex recipe.output.tmp_file={build.project_name}.hex @@ -77,7 +80,7 @@ recipe.output.save_file={build.project_name}.{build.variant}.hex ## Compute size recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf" -recipe.size.regex=^(?:\.text|\.data|\.bootloader)\s+([0-9]+).* +recipe.size.regex=^(?:\.text|\.data|\.rodata|\.bootloader)\s+([0-9]+).* recipe.size.regex.data=^(?:\.data|\.bss|\.noinit)\s+([0-9]+).* recipe.size.regex.eeprom=^(?:\.eeprom)\s+([0-9]+).* @@ -88,28 +91,32 @@ recipe.preproc.includes="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} preproc.macros.flags=-w -x c++ -E -CC recipe.preproc.macros="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} {preproc.macros.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} "-I{build.core.path}/api/deprecated" {includes} "{source_file}" -o "{preprocessed_file_path}" +# Required discoveries and monitors +# --------------------------------- +pluggable_discovery.required.0=builtin:serial-discovery +pluggable_discovery.required.1=builtin:mdns-discovery +pluggable_monitor.required.serial=builtin:serial-monitor + # AVR Uploader/Programmers tools # ------------------------------ -tools.avrdude.path={runtime.tools.avrdude-6.3.0-arduino14.path} +tools.avrdude.path={runtime.tools.avrdude-6.3.0-arduino17.path} tools.avrdude.cmd.path={path}/bin/avrdude tools.avrdude.config.path={path}/etc/avrdude.conf -tools.avrdude.network_cmd={runtime.tools.arduinoOTA.path}/bin/arduinoOTA - tools.avrdude.upload.params.verbose=-v tools.avrdude.upload.params.quiet=-q -q # tools.avrdude.upload.verify is needed for backwards compatibility with IDE 1.6.8 or older, IDE 1.6.9 or newer overrides this value tools.avrdude.upload.verify= tools.avrdude.upload.params.noverify=-V -tools.avrdude.upload.pattern="{cmd.path}" "-C{config.path}" {upload.verbose} {upload.verify} -p{build.mcu} -c{upload.protocol} -Pusb -b{upload.speed} -e -D "-Uflash:w:{build.path}/{build.project_name}.hex:i" "-Ufuse5:w:{bootloader.SYSCFG0}:m" "-Ufuse8:w:{bootloader.BOOTEND}:m" +tools.avrdude.upload.pattern="{cmd.path}" "-C{config.path}" {upload.verbose} {upload.verify} -p{build.mcu} -c{upload.protocol} {upload.extra_params} -b{upload.speed} -e -D "-Uflash:w:{build.path}/{build.project_name}.hex:i" "-Ufuse2:w:{bootloader.OSCCFG}:m" "-Ufuse5:w:{bootloader.SYSCFG0}:m" "-Ufuse8:w:{bootloader.BOOTEND}:m" {upload.extra_files} tools.avrdude.program.params.verbose=-v tools.avrdude.program.params.quiet=-q -q # tools.avrdude.program.verify is needed for backwards compatibility with IDE 1.6.8 or older, IDE 1.6.9 or newer overrides this value tools.avrdude.program.verify= tools.avrdude.program.params.noverify=-V -tools.avrdude.program.pattern="{cmd.path}" "-C{config.path}" {program.verbose} {program.verify} -p{build.mcu} -c{protocol} {program.extra_params} "-Uflash:w:{build.path}/{build.project_name}.hex:i" "-Ufuse5:w:{bootloader.SYSCFG0}:m" "-Ufuse8:w:{bootloader.BOOTEND}:m" +tools.avrdude.program.pattern="{cmd.path}" "-C{config.path}" {program.verbose} {program.verify} -p{build.mcu} -c{protocol} {program.extra_params} "-Uflash:w:{build.path}/{build.project_name}.hex:i" "-Ufuse2:w:{bootloader.OSCCFG}:m" "-Ufuse5:w:{bootloader.SYSCFG0}:m" "-Ufuse8:w:{bootloader.BOOTEND}:m" {upload.extra_files} tools.avrdude.erase.params.verbose=-v tools.avrdude.erase.params.quiet=-q -q @@ -117,11 +124,20 @@ tools.avrdude.erase.pattern= tools.avrdude.bootloader.params.verbose=-v tools.avrdude.bootloader.params.quiet=-q -q -tools.avrdude.bootloader.pattern="{cmd.path}" "-C{config.path}" {bootloader.verbose} -p{build.mcu} -c{protocol} {program.extra_params} "-Ufuse5:w:{bootloader.SYSCFG0}:m" "-Ufuse8:w:{bootloader.BOOTEND}:m" +tools.avrdude.bootloader.pattern="{cmd.path}" "-C{config.path}" {bootloader.verbose} -p{build.mcu} -c{protocol} {program.extra_params} "-Ufuse2:w:{bootloader.OSCCFG}:m" "-Ufuse5:w:{bootloader.SYSCFG0}:m" "-Ufuse8:w:{bootloader.BOOTEND}:m" "-Uflash:w:{runtime.platform.path}/bootloaders/{bootloader.file}:i" tools.avrdude_remote.upload.pattern=/usr/bin/run-avrdude /tmp/sketch.hex {upload.verbose} -p{build.mcu} -tools.avrdude.upload.network_pattern="{network_cmd}" -address {serial.port} -port {upload.network.port} -sketch "{build.path}/{build.project_name}.hex" -upload {upload.network.endpoint_upload} -sync {upload.network.endpoint_sync} -reset {upload.network.endpoint_reset} -sync_exp {upload.network.sync_return} +# The following rule is deprecated by pluggable discovery. +# We keep it to avoid breaking compatibility with the Arduino Java IDE. +tools.avrdude.network_cmd={runtime.tools.arduinoOTA.path}/bin/arduinoOTA +tools.avrdude.upload.network_pattern="{network_cmd}" -address {serial.port} -port 65280 -username arduino -password "{network.password}" -sketch "{build.path}/{build.project_name}.bin" -upload /sketch -d -v + +# ArduinoOTA +tools.arduino_ota.cmd={runtime.tools.arduinoOTA-1.3.0.path}/bin/arduinoOTA +tools.arduino_ota.upload.field.password=Password +tools.arduino_ota.upload.field.password.secret=true +tools.arduino_ota.upload.pattern="{tools.arduino_ota.cmd}" -address {upload.port.address} -port 65280 -username arduino -password "{upload.field.password}" -sketch "{build.path}/{build.project_name}.bin" -upload /sketch -d -v # USB Default Flags # Default blank usb manufacturer will be filled in at compile time diff --git a/post_install.sh b/post_install.sh new file mode 100644 index 00000000..9a944e8a --- /dev/null +++ b/post_install.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +megaAVR_rules () { + echo "" + echo "# Arduino Mega AVR boards bootloader mode udev rules" + echo "" +cat < /etc/udev/rules.d/60-arduino-megaAVR.rules + +# reload udev rules +echo "Reload rules..." +udevadm control --reload-rules +udevadm trigger + diff --git a/programmers.txt b/programmers.txt index d9e83344..70af3a0a 100644 --- a/programmers.txt +++ b/programmers.txt @@ -3,4 +3,5 @@ medbg.communication=usb medbg.protocol=xplainedmini_updi medbg.program.protocol=xplainedmini_updi medbg.program.tool=avrdude -medbg.program.extra_params=-Pusb \ No newline at end of file +medbg.program.tool.default=avrdude +medbg.program.extra_params=-Pusb diff --git a/variants/nona4809/pins_arduino.h b/variants/nona4809/pins_arduino.h new file mode 100644 index 00000000..589dc2ae --- /dev/null +++ b/variants/nona4809/pins_arduino.h @@ -0,0 +1,297 @@ +/* + pins_arduino.h - Pin definition functions for Arduino + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2007 David A. Mellis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "timers.h" + +#define NUM_DIGITAL_PINS 22 // (14 on digital headers + 8 on analog headers) +#define NUM_ANALOG_INPUTS 14 +#define NUM_RESERVED_PINS 6 // (TOSC1/2, VREF, RESET, DEBUG USART Rx/Tx) +#define NUM_INTERNALLY_USED_PINS 10 // (2 x Chip select + 2 x UART + 4 x IO + LED_BUILTIN + 1 unused pin) +#define NUM_I2C_PINS 2 // (SDA / SCL) +#define NUM_SPI_PINS 3 // (MISO / MOSI / SCK) +#define NUM_TOTAL_FREE_PINS (NUM_DIGITAL_PINS) +#define NUM_TOTAL_PINS (NUM_DIGITAL_PINS + NUM_RESERVED_PINS + NUM_INTERNALLY_USED_PINS + NUM_I2C_PINS + NUM_SPI_PINS) +#define ANALOG_INPUT_OFFSET 14 + +#define EXTERNAL_NUM_INTERRUPTS 48 + +#define digitalPinHasPWM(p) ((p) == 3 || (p) == 5 || (p) == 6 || (p) == 9 || (p) == 10) + +#define SPI_MUX (PORTMUX_SPI0_ALT2_gc) +#define PIN_SPI_MISO (12) +#define PIN_SPI_SCK (13) +#define PIN_SPI_MOSI (11) +#define PIN_SPI_SS (8) + +static const uint8_t SS = PIN_SPI_SS; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +#define PIN_WIRE_SDA (22) +#define PIN_WIRE_SCL (23) + +static const uint8_t SDA = PIN_WIRE_SDA; +static const uint8_t SCL = PIN_WIRE_SCL; + +// Main USART available on Arduino header pins +// USART1 on mega4809 (alternative pins) +// Mapped to HWSERIAL0 in Serial library +#define HWSERIAL1 (&USART1) +#define HWSERIAL1_DRE_VECTOR (USART1_DRE_vect) +#define HWSERIAL1_DRE_VECTOR_NUM (USART1_DRE_vect_num) +#define HWSERIAL1_RXC_VECTOR (USART1_RXC_vect) +#define HWSERIAL1_MUX (PORTMUX_USART1_ALT1_gc) +#define PIN_WIRE_HWSERIAL1_RX (0) +#define PIN_WIRE_HWSERIAL1_TX (1) + +// Uno2 Debug USART (not available on headers, only via the EDGB virtual COM port) +// USART3 on mega4809 (alternative pins) +// Mapped to HWSERIAL1 in Serial library +#define HWSERIAL0 (&USART3) +#define HWSERIAL0_DRE_VECTOR (USART3_DRE_vect) +#define HWSERIAL0_DRE_VECTOR_NUM (USART3_DRE_vect_num) +#define HWSERIAL0_RXC_VECTOR (USART3_RXC_vect) +#define HWSERIAL0_MUX (PORTMUX_USART3_ALT1_gc) +#define PIN_WIRE_HWSERIAL0_RX (24) +#define PIN_WIRE_HWSERIAL0_TX (25) + +#define HWSERIAL2_MUX (PORTMUX_USART0_NONE_gc) +#define HWSERIAL3_MUX (PORTMUX_USART2_NONE_gc) + +#define TWI_MUX (PORTMUX_TWI0_DEFAULT_gc) //PORTMUX_TWI0_ALT1_gc + +#define MUX_SPI (SPI_MUX) +#define SPI_INTERFACES_COUNT 1 + +#define LED_BUILTIN (13) + +#define PIN_A0 (14) // AIN3 +#define PIN_A1 (15) // AIN2 +#define PIN_A2 (16) // AIN1 +#define PIN_A3 (17) // AIN0 +#define PIN_A4 (18) // PF2 / AIN12 +#define PIN_A5 (19) // PF3 / AIN13 +#define PIN_A6 (20) // AIN5 +#define PIN_A7 (21) // AIN4 + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; +static const uint8_t A4 = PIN_A4; +static const uint8_t A5 = PIN_A5; +static const uint8_t A6 = PIN_A6; +static const uint8_t A7 = PIN_A7; + +#define PINS_COUNT (40u) + +#ifdef ARDUINO_MAIN + +// On the Arduino board, digital pins are also used +// for the analog output (software PWM). Analog input +// pins are a separate set. + +// ATMEGA4809 / ARDUINO +// +// (SCL)(SDA) (7) (2) (R) (3~) (6~) +// PA4 PA3 PA2 PA1 PA0 GND VDD UPDI PF6 PF5 PF4 PF3 +// +// 48 47 46 45 44 43 42 41 40 39 38 37 +// + ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ + +// PA5 1| |36 PF2 +// PA6 2| |35 PF1 (TOSC2) +// PA7 3| |34 PF0 (TOSC1) +// (9~) PB0 4| |33 PE3 (8) +// (10~) PB1 5| |32 PE2 (13) +// (5~) PB2 6| |31 PE1 (12) +// PB3 7| 48pin QFN |30 PE0 (11~) +// (Tx) PB4 8| |29 GND +// (Rx) PB5 9| |28 AVDD +// PC0 10| |27 PD7 (VREF) +// PC1 11| |26 PD6 +// PC2 12| |25 PD5 (A5) +// + ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ + +// 13 14 15 16 17 18 19 20 21 22 23 24 +// +// PC3 VDD GND PC4 PC5 PC6 PC7 PD0 PD1 PD2 PD3 PD4 +// (1) (0) (4) (A0) (A1) (A2) (A3) (A4) + +// + +const uint8_t digital_pin_to_port[] = { + PC, // 0 PC5/USART1_Rx + PC, // 1 PC4/USART1_Tx + PA, // 2 PA0 + PF, // 3 PF5 + PC, // 4 PC6 + PB, // 5 PB2 + PF, // 6 PF4 + PA, // 7 PA1 + PE, // 8 PE3 + PB, // 9 PB0 + PB, // 10 PB1 + PE, // 11 PE0 + PE, // 12 PE1 + PE, // 13 PE2 + PD, // 14 PD3/AI3 + PD, // 15 PD2/AI2 + PD, // 16 PD1/AI1 + PD, // 17 PD0/AI0 + PF, // 18 PF2/AI12 + PF, // 19 PF3/AI13 + PD, // 20 PD4/AI4 + PD, // 21 PD5/AI5 + PA, // 22 PA2/TWI_SDA + PA, // 23 PA3/TWI_SCL + PB, // 24 PB5/USART3_Rx + PB, // 25 PB4/USART3_Tx +}; + +/* Use this for accessing PINnCTRL register */ +const uint8_t digital_pin_to_bit_position[] = { + PIN5_bp, // 0 PC5/USART1_Rx + PIN4_bp, // 1 PC4/USART1_Tx + PIN0_bp, // 2 PA0 + PIN5_bp, // 3 PF5 + PIN6_bp, // 4 PC6 + PIN2_bp, // 5 PB2 + PIN4_bp, // 6 PF4 + PIN1_bp, // 7 PA1 + PIN3_bp, // 8 PE3 + PIN0_bp, // 9 PB0 + PIN1_bp, // 10 PB1 + PIN0_bp, // 11 PE0 + PIN1_bp, // 12 PE1 + PIN2_bp, // 13 PE2 + PIN3_bp, // 14 PD3/AI3 + PIN2_bp, // 15 PD2/AI2 + PIN1_bp, // 16 PD1/AI1 + PIN0_bp, // 17 PD0/AI0 + PIN2_bp, // 18 PF2/AI12 + PIN3_bp, // 19 PF3/AI13 + PIN4_bp, // 20 PD4/AI4 + PIN5_bp, // 21 PD5/AI5 + PIN2_bp, // 22 PA2/TWI_SDA + PIN3_bp, // 23 PA3/TWI_SCL + PIN5_bp, // 24 PB5/USART3_Rx + PIN4_bp, // 25 PB4/USART3_Tx +}; + +/* Use this for accessing PINnCTRL register */ +const uint8_t digital_pin_to_bit_mask[] = { + PIN5_bm, // 0 PC5/USART1_Rx + PIN4_bm, // 1 PC4/USART1_Tx + PIN0_bm, // 2 PA0 + PIN5_bm, // 3 PF5 + PIN6_bm, // 4 PC6 + PIN2_bm, // 5 PB2 + PIN4_bm, // 6 PF4 + PIN1_bm, // 7 PA1 + PIN3_bm, // 8 PE3 + PIN0_bm, // 9 PB0 + PIN1_bm, // 10 PB1 + PIN0_bm, // 11 PE0 + PIN1_bm, // 12 PE1 + PIN2_bm, // 13 PE2 + PIN3_bm, // 14 PD3/AI3 + PIN2_bm, // 15 PD2/AI2 + PIN1_bm, // 16 PD1/AI1 + PIN0_bm, // 17 PD0/AI0 + PIN2_bm, // 18 PF2/AI12 + PIN3_bm, // 19 PF3/AI13 + PIN4_bm, // 20 PD4/AI4 + PIN5_bm, // 21 PD5/AI5 + PIN2_bm, // 22 PA2/TWI_SDA + PIN3_bm, // 23 PA3/TWI_SCL + PIN5_bm, // 24 PB5/USART3_Rx + PIN4_bm, // 25 PB4/USART3_Tx +}; + +const uint8_t digital_pin_to_timer[] = { + NOT_ON_TIMER, // 0 PC5/USART1_Rx + NOT_ON_TIMER, // 1 PC4/USART1_Tx + NOT_ON_TIMER, // 2 PA0 + TIMERB1, // 3 PF5 + NOT_ON_TIMER, // 4 PC6 + TIMERA0, // 5 PB2 + TIMERB0, // 6 PF4 + NOT_ON_TIMER, // 7 PA1 + NOT_ON_TIMER, // 8 PE3 + TIMERA0, // 9 PB0 + TIMERA0, // 10 PB1 + NOT_ON_TIMER, // 11 PE0 + NOT_ON_TIMER, // 12 PE1 + NOT_ON_TIMER, // 13 PE2 + NOT_ON_TIMER, // 14 PD3/AI3 + NOT_ON_TIMER, // 15 PD2/AI2 + NOT_ON_TIMER, // 16 PD1/AI1 + NOT_ON_TIMER, // 17 PD0/AI0 + NOT_ON_TIMER, // 18 PF2/AI12 + NOT_ON_TIMER, // 19 PF3/AI13 + NOT_ON_TIMER, // 20 PD4/AI4 + NOT_ON_TIMER, // 21 PD5/AI5 + NOT_ON_TIMER, // 22 PA2/TWI_SDA + NOT_ON_TIMER, // 23 PA3/TWI_SCL + NOT_ON_TIMER, // 24 PB5/USART3_Rx + NOT_ON_TIMER, // 25 PB4/USART3_Tx +}; + +const uint8_t analog_pin_to_channel[] = { + 3, + 2, + 1, + 0, + 12, + 13, + 4, + 5 +}; + +#endif + +#define digitalPinToAnalogInput(p) ((p < ANALOG_INPUT_OFFSET) ? analog_pin_to_channel[p] : analog_pin_to_channel[p - ANALOG_INPUT_OFFSET] ) + +// These serial port names are intended to allow libraries and architecture-neutral +// sketches to automatically default to the correct port name for a particular type +// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN, +// the first hardware serial port whose RX/TX pins are not dedicated to another use. +// +// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor +// +// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial +// +// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library +// +// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins. +// +// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX +// pins are NOT connected to anything by default. +#define SERIAL_PORT_MONITOR Serial +#define SERIAL_PORT_HARDWARE Serial1 +#define SERIAL_PORT_USBVIRTUAL Serial + +#endif diff --git a/variants/nona4809/timers.h b/variants/nona4809/timers.h new file mode 100644 index 00000000..438e79fb --- /dev/null +++ b/variants/nona4809/timers.h @@ -0,0 +1,12 @@ +#ifndef __TIMERS_H__ +#define __TIMERS_H__ + +#define TIME_TRACKING_TIMER_PERIOD 0xFF +#define TIME_TRACKING_TICKS_PER_OVF (TIME_TRACKING_TIMER_PERIOD + 1) // Timer ticks per overflow of TCB3 +#define TIME_TRACKING_TIMER_DIVIDER 64 // Clock divider for TCB0 +#define TIME_TRACKING_CYCLES_PER_OVF (TIME_TRACKING_TICKS_PER_OVF * TIME_TRACKING_TIMER_DIVIDER) + +#define PWM_TIMER_PERIOD 0xFF // For frequency +#define PWM_TIMER_COMPARE 0x80 // For duty cycle + +#endif \ No newline at end of file diff --git a/variants/nona4809/variant.c b/variants/nona4809/variant.c new file mode 100644 index 00000000..d727ec72 --- /dev/null +++ b/variants/nona4809/variant.c @@ -0,0 +1,117 @@ +#include + +#include "pins_arduino.h" +#include "api/Common.h" + +#define FORCE_INLINE __attribute__((always_inline)) inline + +void setup_timers() { + + // TYPE A TIMER + + // PORTMUX setting for TCA -> all outputs [0:2] point to PORTB pins [0:2] + PORTMUX.TCAROUTEA = PORTMUX_TCA0_PORTB_gc; + + // Setup timers for single slope PWM, but do not enable, will do in analogWrite() + TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_SINGLESLOPE_gc; + + // Period setting, 16 bit register but val resolution is 8 bit + TCA0.SINGLE.PER = PWM_TIMER_PERIOD; + + // Default duty 50%, will re-assign in analogWrite() + TCA0.SINGLE.CMP0BUF = PWM_TIMER_COMPARE; + TCA0.SINGLE.CMP1BUF = PWM_TIMER_COMPARE; + TCA0.SINGLE.CMP2BUF = PWM_TIMER_COMPARE; + + // Use DIV64 prescaler (giving 250kHz clock), enable TCA timer + TCA0.SINGLE.CTRLA = (TCA_SINGLE_CLKSEL_DIV64_gc) | (TCA_SINGLE_ENABLE_bm); + + // TYPE B TIMERS + + // Setup TCB0 routing + #if defined(TCB0) + PORTMUX.TCBROUTEA |= PORTMUX_TCB0_bm; // Route signal to PF4 + #endif + + // Setup TCB1 routing + #if defined(TCB1) + PORTMUX.TCBROUTEA |= PORTMUX_TCB1_bm; // Route signal to PF5 + #endif + + // Start with TCB0 + TCB_t *timer_B = (TCB_t *)&TCB0; + + // Find end timer + #if defined(TCB3) + TCB_t *timer_B_end = (TCB_t *)&TCB3; + #elif defined(TCB2) + TCB_t *timer_B_end = (TCB_t *)&TCB2; + #elif defined(TCB1) + TCB_t *timer_B_end = (TCB_t *)&TCB1; + #else + TCB_t *timer_B_end = (TCB_t *)&TCB0; + #endif + + // Timer B Setup loop for TCB[0:3] + do + { + // 8 bit PWM mode, but do not enable output yet, will do in analogWrite() + timer_B->CTRLB = (TCB_CNTMODE_PWM8_gc); + + // Assign 8-bit period + timer_B->CCMPL = PWM_TIMER_PERIOD; + + // default duty 50%, set when output enabled + timer_B->CCMPH = PWM_TIMER_COMPARE; + + // Use TCA clock (250kHz) and enable + // (sync update commented out, might try to synchronize later + timer_B->CTRLA = (TCB_CLKSEL_CLKTCA_gc) + //|(TCB_SYNCUPD_bm) + |(TCB_ENABLE_bm); + + // Increment pointer to next TCB instance + timer_B++; + + // Stop when pointing to TCB3 + } while (timer_B <= timer_B_end); + + // Stuff for synchronizing PWM timers +// // Restart TCA to sync TCBs +// // should not be needed +// TCA0.SINGLE.CTRLESET = TCA_SINGLE_CMD_RESTART_gc; +// TCA0.SINGLE.CTRLECLR = TCA_SINGLE_CMD_RESTART_gc; +// +// timer_B = (TCB_t *)&TCB0; +// +// // TCB are sync to TCA, remove setting +// for (uint8_t digitial_pin_timer = (TIMERB0 - TIMERB0); +// digitial_pin_timer < (TIMERB3 - TIMERB0); +// digitial_pin_timer++) +// { +// // disable sync with tca +// timer_B->CTRLA &= ~ (TCB_SYNCUPD_bm); +// +// // Add offset to register +// timer_B++; +// +// } +} + +FORCE_INLINE bool isDoubleBondedActive(uint8_t pin) { + (void)pin; + + /* Check if TWI is operating on double bonded pin (Master Enable is high + in both Master and Slave mode for bus error detection, so this can + indicate an active state for Wire) */ + //if(((pin == PIN_A4) || (pin == PIN_A5)) && (TWI0.MCTRLA & TWI_ENABLE_bm)) return true; + + /* Special check for SPI_SS double bonded pin -- no action if SPI is active + (Using SPI Enable bit as indicator of SPI activity) */ + //if((pin == 10) && (SPI0.CTRLA & SPI_ENABLE_bm)) return true; + + return false; +} + +void initVariant() { +} diff --git a/variants/uno2018/pins_arduino.h b/variants/uno2018/pins_arduino.h index de14464a..3b7e3aae 100644 --- a/variants/uno2018/pins_arduino.h +++ b/variants/uno2018/pins_arduino.h @@ -45,7 +45,7 @@ #define PIN_SPI_MISO (33) #define PIN_SPI_SCK (34) #define PIN_SPI_MOSI (32) -#define PIN_SPI_SS (10) +#define PIN_SPI_SS (22) static const uint8_t SS = PIN_SPI_SS; static const uint8_t MOSI = PIN_SPI_MOSI; @@ -163,7 +163,7 @@ static const uint8_t A5 = PIN_A5; // -const uint8_t PROGMEM digital_pin_to_port[] = { +const uint8_t digital_pin_to_port[] = { PC, // 0 PC5/USART1_Rx PC, // 1 PC4/USART1_Tx PA, // 2 PA0 @@ -208,7 +208,7 @@ const uint8_t PROGMEM digital_pin_to_port[] = { }; /* Use this for accessing PINnCTRL register */ -const uint8_t PROGMEM digital_pin_to_bit_position[] = { +const uint8_t digital_pin_to_bit_position[] = { PIN5_bp, // 0 PC5/USART1_Rx PIN4_bp, // 1 PC4/USART1_Tx PIN0_bp, // 2 PA0 @@ -253,7 +253,7 @@ const uint8_t PROGMEM digital_pin_to_bit_position[] = { }; /* Use this for accessing PINnCTRL register */ -const uint8_t PROGMEM digital_pin_to_bit_mask[] = { +const uint8_t digital_pin_to_bit_mask[] = { PIN5_bm, // 0 PC5/USART1_Rx PIN4_bm, // 1 PC4/USART1_Tx PIN0_bm, // 2 PA0 @@ -297,7 +297,7 @@ const uint8_t PROGMEM digital_pin_to_bit_mask[] = { PIN6_bm // 40 PF6 RESET }; -const uint8_t PROGMEM digital_pin_to_timer[] = { +const uint8_t digital_pin_to_timer[] = { NOT_ON_TIMER, // 0 PC5/USART1_Rx NOT_ON_TIMER, // 1 PC4/USART1_Tx NOT_ON_TIMER, // 2 PA0