mirror of
https://github.com/hathach/tinyusb.git
synced 2025-03-21 07:21:00 +00:00
Merge pull request #80 from hathach/develop
added usbd_edpt_xfer/usbd_edpt_busy to replace dcd_edpt_transfer/dcd_edpt_busy()
This commit is contained in:
commit
393492823c
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,11 +1,9 @@
|
||||
/.metadata
|
||||
html
|
||||
latex
|
||||
test_old
|
||||
tests/build
|
||||
test/_build
|
||||
*.d
|
||||
*.o
|
||||
*.launch
|
||||
*.P
|
||||
*.map
|
||||
*.axf
|
||||
*.jlink
|
||||
@ -13,6 +11,4 @@ tests/build
|
||||
*.elf
|
||||
*.ind
|
||||
.env
|
||||
/tests/lpc175x_6x/build/
|
||||
/tests/lpc18xx_43xx/build/
|
||||
/examples/*/*/build-*
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -4,9 +4,6 @@
|
||||
[submodule "hw/mcu/microchip/samd/asf4"]
|
||||
path = hw/mcu/microchip/samd/asf4
|
||||
url = https://github.com/adafruit/asf4.git
|
||||
[submodule "tests/vendor/unity"]
|
||||
path = tests/vendor/unity
|
||||
url = https://github.com/ThrowTheSwitch/Unity.git
|
||||
[submodule "hw/mcu/st/stm32lib"]
|
||||
path = hw/mcu/st/stm32lib
|
||||
url = https://github.com/hathach/stm32lib.git
|
||||
|
28
.travis.yml
28
.travis.yml
@ -2,19 +2,25 @@ language: c
|
||||
dist: xenial
|
||||
compiler:
|
||||
- gcc
|
||||
env:
|
||||
- TRAVIS_SDK=arm
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: "ppa:team-gcc-arm-embedded/ppa"
|
||||
packages:
|
||||
- python3
|
||||
- ruby
|
||||
- gcc-arm-embedded
|
||||
|
||||
install:
|
||||
- gem install ceedling
|
||||
|
||||
before_script:
|
||||
- function var_search () { case "$1" in *$2*) true;; *) false;; esac; }
|
||||
- sudo dpkg --add-architecture i386
|
||||
|
||||
- (! var_search "${TRAVIS_SDK-}" arm || (wget https://s3.amazonaws.com/adafruit-circuit-python/gcc-arm-embedded_7-2018q2-1~trusty1_amd64.deb && sudo dpkg -i gcc-arm-embedded*_amd64.deb))
|
||||
- sudo apt-get install -y realpath
|
||||
|
||||
- realpath --version
|
||||
- gcc --version
|
||||
- (! var_search "${TRAVIS_SDK-}" arm || arm-none-eabi-gcc --version)
|
||||
- arm-none-eabi-gcc --version
|
||||
|
||||
script:
|
||||
# Build all examples
|
||||
- python3 tools/build_all.py
|
||||
# Run unit tests
|
||||
- cd test
|
||||
- ceedling test:all
|
||||
|
@ -15,7 +15,7 @@ TinyUSB is an open-source cross-platform USB Host/Device stack for embedded syst
|
||||
│ └── mcu # Low level mcu core & peripheral drivers
|
||||
├── lib # Sources from 3rd party such as freeRTOS, fatfs ...
|
||||
├── src # All sources files for TinyUSB stack itself.
|
||||
├── tests # Unit tests for the stack
|
||||
├── test # Unit tests for the stack
|
||||
└── tools # Files used internally
|
||||
```
|
||||
|
||||
@ -77,6 +77,7 @@ TinyUSB is currently used by these other projects:
|
||||
|
||||
* [Adafruit nRF52 Arduino](https://github.com/adafruit/Adafruit_nRF52_Arduino)
|
||||
* [Adafruit nRF52 Bootloader](https://github.com/adafruit/Adafruit_nRF52_Bootloader)
|
||||
* [Adafruit SAMD Arduino](https://github.com/adafruit/ArduinoCore-samd)
|
||||
* [CircuitPython](https://github.com/adafruit/circuitpython)
|
||||
* [TinyUSB Arduino Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino)
|
||||
|
||||
|
@ -201,7 +201,7 @@ void hid_task(void)
|
||||
tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0);
|
||||
|
||||
// delay a bit before attempt to send keyboard report
|
||||
board_delay(2);
|
||||
board_delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4)
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -95,6 +95,9 @@
|
||||
// Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_BUFSIZE 16
|
||||
|
||||
#define CFG_TUD_MIDI_RX_BUFSIZE 512
|
||||
#define CFG_TUD_MIDI_TX_BUFSIZE 512
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -62,7 +62,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4)
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -34,9 +34,18 @@
|
||||
* It will receive data from Host (In endpoint) and echo back (Out endpoint).
|
||||
* HID Report descriptor use vendor for usage page (using template TUD_HID_REPORT_DESC_GENERIC_INOUT)
|
||||
*
|
||||
* Run 'python3 hid_test.py' on your PC to send and receive data to this device.
|
||||
* Python and `hid` package is required, for installation please follow
|
||||
* https://pypi.org/project/hid/
|
||||
* There are 2 ways to test the sketch
|
||||
* 1. Using nodejs
|
||||
* - Install nodejs and nmp to your PC
|
||||
* - Install execellent node-hid (https://github.com/node-hid/node-hid) by
|
||||
* $ npm install node-hid
|
||||
* - Run provided hid test script
|
||||
* $ node hid_test.js
|
||||
*
|
||||
* 2. Using python hidRun
|
||||
* - Python and `hid` package is required, for installation please follow https://pypi.org/project/hid/
|
||||
* - Run provided hid test script to send and receive data to this device.
|
||||
* $ python3 hid_test.py
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -62,7 +62,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4)
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -62,7 +62,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4)
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -63,7 +63,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4)
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -14,6 +14,13 @@ CP = cp
|
||||
RM = rm
|
||||
PYTHON ?= python
|
||||
|
||||
check_defined = \
|
||||
$(strip $(foreach 1,$1, \
|
||||
$(call __check_defined,$1,$(strip $(value 2)))))
|
||||
__check_defined = \
|
||||
$(if $(value $1),, \
|
||||
$(error Undefined make flag: $1$(if $2, ($2))))
|
||||
|
||||
# Select the board to build for.
|
||||
ifeq ($(BOARD),)
|
||||
$(info You must provide a BOARD parameter with 'BOARD=')
|
||||
|
@ -67,7 +67,7 @@
|
||||
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X
|
||||
#define CFG_TUSB_MEM_SECTION // LPC17xx USB DMA can access all address
|
||||
#elif (CFG_TUSB_MCU == OPT_MCU_LPC43XX)
|
||||
#define CFG_TUSB_MEM_SECTION ATTR_SECTION(.data.$RAM3)
|
||||
#define CFG_TUSB_MEM_SECTION TU_ATTR_SECTION(.data.$RAM3)
|
||||
#endif
|
||||
|
||||
#elif defined __CC_ARM // Compiled with Keil armcc
|
||||
|
@ -13,6 +13,7 @@ SRC_C += \
|
||||
src/class/msc/msc_device.c \
|
||||
src/class/cdc/cdc_device.c \
|
||||
src/class/hid/hid_device.c \
|
||||
src/class/midi/midi_device.c \
|
||||
src/tusb.c \
|
||||
src/portable/$(VENDOR)/$(CHIP_FAMILY)/dcd_$(CHIP_FAMILY).c
|
||||
|
||||
|
@ -98,7 +98,11 @@ static inline void board_led_off(void)
|
||||
static inline void board_delay(uint32_t ms)
|
||||
{
|
||||
uint32_t start_ms = board_millis();
|
||||
while( board_millis() - start_ms < ms) {}
|
||||
while (board_millis() - start_ms < ms)
|
||||
{
|
||||
// take chance to run usb background
|
||||
tud_task();
|
||||
}
|
||||
}
|
||||
|
||||
static inline int8_t board_uart_getchar(void)
|
||||
|
@ -50,5 +50,10 @@ JLINK_IF = swd
|
||||
# For uf2 conversion
|
||||
UF2_FAMILY = 0xADA52840
|
||||
|
||||
# flash using jlink
|
||||
flash: flash-jlink
|
||||
$(BUILD)/$(BOARD)-firmware.zip: $(BUILD)/$(BOARD)-firmware.hex
|
||||
adafruit-nrfutil dfu genpkg --dev-type 0x0052 --sd-req 0xFFFE --application $^ $@
|
||||
|
||||
# flash using adafruit-nrfutil dfu
|
||||
flash: $(BUILD)/$(BOARD)-firmware.zip
|
||||
@:$(call check_defined, SERIAL, example: SERIAL=/dev/ttyACM0)
|
||||
adafruit-nrfutil --verbose dfu serial --package $^ -p $(SERIAL) -b 115200 --singlebank --touch 1200
|
||||
|
@ -212,7 +212,7 @@ typedef enum
|
||||
// FUNCTIONAL DESCRIPTOR (COMMUNICATION INTERFACE)
|
||||
//--------------------------------------------------------------------+
|
||||
/// Header Functional Descriptor (Communication Interface)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
@ -221,7 +221,7 @@ typedef struct ATTR_PACKED
|
||||
}cdc_desc_func_header_t;
|
||||
|
||||
/// Union Functional Descriptor (Communication Interface)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
@ -231,7 +231,7 @@ typedef struct ATTR_PACKED
|
||||
}cdc_desc_func_union_t;
|
||||
|
||||
#define cdc_desc_func_union_n_t(no_slave)\
|
||||
struct ATTR_PACKED { \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength ;\
|
||||
uint8_t bDescriptorType ;\
|
||||
uint8_t bDescriptorSubType ;\
|
||||
@ -240,7 +240,7 @@ typedef struct ATTR_PACKED
|
||||
}
|
||||
|
||||
/// Country Selection Functional Descriptor (Communication Interface)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
@ -250,7 +250,7 @@ typedef struct ATTR_PACKED
|
||||
}cdc_desc_func_country_selection_t;
|
||||
|
||||
#define cdc_desc_func_country_selection_n_t(no_country) \
|
||||
struct ATTR_PACKED {\
|
||||
struct TU_ATTR_PACKED {\
|
||||
uint8_t bLength ;\
|
||||
uint8_t bDescriptorType ;\
|
||||
uint8_t bDescriptorSubType ;\
|
||||
@ -264,7 +264,7 @@ typedef struct ATTR_PACKED
|
||||
|
||||
/// \brief Call Management Functional Descriptor
|
||||
/// \details This functional descriptor describes the processing of calls for the Communications Class interface.
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
@ -280,7 +280,7 @@ typedef struct ATTR_PACKED
|
||||
}cdc_desc_func_call_management_t;
|
||||
|
||||
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t support_comm_request : 1; ///< Device supports the request combination of Set_Comm_Feature, Clear_Comm_Feature, and Get_Comm_Feature.
|
||||
uint8_t support_line_request : 1; ///< Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State.
|
||||
@ -293,7 +293,7 @@ TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compile
|
||||
|
||||
/// \brief Abstract Control Management Functional Descriptor
|
||||
/// \details This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
@ -303,7 +303,7 @@ typedef struct ATTR_PACKED
|
||||
|
||||
/// \brief Direct Line Management Functional Descriptor
|
||||
/// \details This functional descriptor describes the commands supported by the Communications Class interface with SubClass code of \ref CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
@ -319,7 +319,7 @@ typedef struct ATTR_PACKED
|
||||
/// \brief Telephone Ringer Functional Descriptor
|
||||
/// \details The Telephone Ringer functional descriptor describes the ringer capabilities supported by the Communications Class interface,
|
||||
/// with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
@ -331,7 +331,7 @@ typedef struct ATTR_PACKED
|
||||
/// \brief Telephone Operational Modes Functional Descriptor
|
||||
/// \details The Telephone Operational Modes functional descriptor describes the operational modes supported by
|
||||
/// the Communications Class interface, with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
@ -347,7 +347,7 @@ typedef struct ATTR_PACKED
|
||||
/// \brief Telephone Call and Line State Reporting Capabilities Descriptor
|
||||
/// \details The Telephone Call and Line State Reporting Capabilities functional descriptor describes the abilities of a
|
||||
/// telephone device to report optional call and line states.
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
@ -371,7 +371,7 @@ static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc)
|
||||
//--------------------------------------------------------------------+
|
||||
// Requests
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint32_t bit_rate;
|
||||
uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits
|
||||
@ -381,7 +381,7 @@ typedef struct ATTR_PACKED
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct");
|
||||
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint16_t dte_is_present : 1; ///< Indicates to DCE if DTE is presentor not. This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR.
|
||||
uint16_t half_duplex_carrier_control : 1;
|
||||
|
@ -73,23 +73,21 @@ typedef struct
|
||||
//--------------------------------------------------------------------+
|
||||
CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
|
||||
|
||||
// TODO will be replaced by dcd_edpt_busy()
|
||||
bool pending_read_from_host;
|
||||
//bool pending_read_from_host; TODO remove
|
||||
static void _prep_out_transaction (uint8_t itf)
|
||||
{
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||
|
||||
// skip if previous transfer not complete
|
||||
// dcd_edpt_busy() doesn't work, probably transfer is complete but not properly handled by the stack
|
||||
// if ( dcd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return;
|
||||
if (pending_read_from_host) return;
|
||||
if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return;
|
||||
//if (pending_read_from_host) return;
|
||||
|
||||
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
||||
uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff);
|
||||
if ( max_read >= CFG_TUD_CDC_EPSIZE )
|
||||
{
|
||||
dcd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE);
|
||||
pending_read_from_host = true;
|
||||
usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE);
|
||||
// pending_read_from_host = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,13 +181,13 @@ uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
|
||||
bool tud_cdc_n_write_flush (uint8_t itf)
|
||||
{
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||
TU_VERIFY( !dcd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in) ); // skip if previous transfer not complete
|
||||
TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in) ); // skip if previous transfer not complete
|
||||
|
||||
uint16_t count = tu_fifo_read_n(&_cdcd_itf[itf].tx_ff, p_cdc->epin_buf, CFG_TUD_CDC_EPSIZE);
|
||||
if ( count )
|
||||
{
|
||||
TU_VERIFY( tud_cdc_n_connected(itf) ); // fifo is empty if not connected
|
||||
TU_ASSERT( dcd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count) );
|
||||
TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count) );
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -298,7 +296,7 @@ bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
|
||||
}
|
||||
|
||||
// Prepare for incoming data
|
||||
pending_read_from_host = false;
|
||||
// pending_read_from_host = false;
|
||||
_prep_out_transaction(cdc_id);
|
||||
|
||||
return true;
|
||||
@ -394,7 +392,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
|
||||
if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
|
||||
|
||||
// prepare for OUT transaction
|
||||
pending_read_from_host = false;
|
||||
// pending_read_from_host = false;
|
||||
_prep_out_transaction(itf);
|
||||
}
|
||||
|
||||
|
@ -91,16 +91,16 @@ static inline bool tud_cdc_write_flush (void)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when received new data
|
||||
ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf);
|
||||
TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf);
|
||||
|
||||
// Invoked when received `wanted_char`
|
||||
ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
|
||||
TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
|
||||
|
||||
// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
|
||||
ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
|
||||
TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
|
||||
|
||||
// Invoked when line coding is change via SET_LINE_CODING
|
||||
ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
|
||||
TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
@ -41,7 +41,7 @@
|
||||
#define RNDIS_MSG_PAYLOAD_MAX (1024*4)
|
||||
|
||||
CFG_TUSB_MEM_SECTION static uint8_t msg_notification[CFG_TUSB_HOST_DEVICE_MAX][8];
|
||||
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(4) static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
|
||||
|
||||
static rndish_data_t rndish_data[CFG_TUSB_HOST_DEVICE_MAX];
|
||||
|
||||
|
@ -70,7 +70,7 @@ bool cusd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf, uint16_
|
||||
(*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
|
||||
|
||||
// TODO Prepare for incoming data
|
||||
// TU_ASSERT( dcd_edpt_xfer(rhport, p_itf->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
|
||||
// TU_ASSERT( usbd_edpt_xfer(rhport, p_itf->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@
|
||||
* @{ */
|
||||
|
||||
/// USB HID Descriptor
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength; /**< Numeric expression that is the total size of the HID descriptor */
|
||||
uint8_t bDescriptorType; /**< Constant name specifying type of HID descriptor. */
|
||||
@ -150,7 +150,7 @@ typedef enum
|
||||
* @{ */
|
||||
|
||||
/// Standard HID Boot Protocol Mouse Report.
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */
|
||||
int8_t x; /**< Current delta x movement of the mouse. */
|
||||
@ -178,7 +178,7 @@ typedef enum
|
||||
* @{ */
|
||||
|
||||
/// Standard HID Boot Protocol Keyboard Report.
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t modifier; /**< Keyboard modifier (KEYBOARD_MODIFER_* masks). */
|
||||
uint8_t reserved; /**< Reserved for OEM use, always set to 0. */
|
||||
@ -598,6 +598,272 @@ enum
|
||||
HID_USAGE_CONSUMER_AC_PAN = 0x0238,
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* ASCII to KEYCODE Conversion
|
||||
* Expand to array of [128][2] (shift, keycode)
|
||||
*
|
||||
* Usage: example to convert input chr into keyboard report (modifier + keycode)
|
||||
*
|
||||
* uint8_t const conv_table[128][2] = { HID_ASCII_TO_KEYCODE };
|
||||
*
|
||||
* uint8_t keycode[6] = { 0 };
|
||||
* uint8_t modifier = 0;
|
||||
*
|
||||
* if ( conv_table[chr][0] ) modifier = KEYBOARD_MODIFIER_LEFTSHIFT;
|
||||
* keycode[0] = conv_table[chr][1];
|
||||
* tud_hid_keyboard_report(report_id, modifier, keycode);
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
#define HID_ASCII_TO_KEYCODE \
|
||||
{0, 0 }, /* 0x00 Null */ \
|
||||
{0, 0 }, /* 0x01 */ \
|
||||
{0, 0 }, /* 0x02 */ \
|
||||
{0, 0 }, /* 0x03 */ \
|
||||
{0, 0 }, /* 0x04 */ \
|
||||
{0, 0 }, /* 0x05 */ \
|
||||
{0, 0 }, /* 0x06 */ \
|
||||
{0, 0 }, /* 0x07 */ \
|
||||
{0, HID_KEY_BACKSPACE }, /* 0x08 Backspace */ \
|
||||
{0, HID_KEY_TAB }, /* 0x09 Tab */ \
|
||||
{0, HID_KEY_RETURN }, /* 0x0A Line Feed */ \
|
||||
{0, 0 }, /* 0x0B */ \
|
||||
{0, 0 }, /* 0x0C */ \
|
||||
{0, HID_KEY_RETURN }, /* 0x0D CR */ \
|
||||
{0, 0 }, /* 0x0E */ \
|
||||
{0, 0 }, /* 0x0F */ \
|
||||
{0, 0 }, /* 0x10 */ \
|
||||
{0, 0 }, /* 0x11 */ \
|
||||
{0, 0 }, /* 0x12 */ \
|
||||
{0, 0 }, /* 0x13 */ \
|
||||
{0, 0 }, /* 0x14 */ \
|
||||
{0, 0 }, /* 0x15 */ \
|
||||
{0, 0 }, /* 0x16 */ \
|
||||
{0, 0 }, /* 0x17 */ \
|
||||
{0, 0 }, /* 0x18 */ \
|
||||
{0, 0 }, /* 0x19 */ \
|
||||
{0, 0 }, /* 0x1A */ \
|
||||
{0, HID_KEY_ESCAPE }, /* 0x1B Escape */ \
|
||||
{0, 0 }, /* 0x1C */ \
|
||||
{0, 0 }, /* 0x1D */ \
|
||||
{0, 0 }, /* 0x1E */ \
|
||||
{0, 0 }, /* 0x1F */ \
|
||||
\
|
||||
{0, HID_KEY_SPACE }, /* 0x20 */ \
|
||||
{1, HID_KEY_1 }, /* 0x21 ! */ \
|
||||
{1, HID_KEY_APOSTROPHE }, /* 0x22 " */ \
|
||||
{1, HID_KEY_3 }, /* 0x23 # */ \
|
||||
{1, HID_KEY_4 }, /* 0x24 $ */ \
|
||||
{1, HID_KEY_5 }, /* 0x25 % */ \
|
||||
{1, HID_KEY_7 }, /* 0x26 & */ \
|
||||
{0, HID_KEY_APOSTROPHE }, /* 0x27 ' */ \
|
||||
{1, HID_KEY_9 }, /* 0x28 ( */ \
|
||||
{1, HID_KEY_0 }, /* 0x29 ) */ \
|
||||
{1, HID_KEY_8 }, /* 0x2A * */ \
|
||||
{1, HID_KEY_EQUAL }, /* 0x2B + */ \
|
||||
{0, HID_KEY_COMMA }, /* 0x2C , */ \
|
||||
{0, HID_KEY_MINUS }, /* 0x2D - */ \
|
||||
{0, HID_KEY_PERIOD }, /* 0x2E . */ \
|
||||
{0, HID_KEY_SLASH }, /* 0x2F / */ \
|
||||
{0, HID_KEY_0 }, /* 0x30 0 */ \
|
||||
{0, HID_KEY_1 }, /* 0x31 1 */ \
|
||||
{0, HID_KEY_2 }, /* 0x32 2 */ \
|
||||
{0, HID_KEY_3 }, /* 0x33 3 */ \
|
||||
{0, HID_KEY_4 }, /* 0x34 4 */ \
|
||||
{0, HID_KEY_5 }, /* 0x35 5 */ \
|
||||
{0, HID_KEY_6 }, /* 0x36 6 */ \
|
||||
{0, HID_KEY_7 }, /* 0x37 7 */ \
|
||||
{0, HID_KEY_8 }, /* 0x38 8 */ \
|
||||
{0, HID_KEY_9 }, /* 0x39 9 */ \
|
||||
{1, HID_KEY_SEMICOLON }, /* 0x3A : */ \
|
||||
{0, HID_KEY_SEMICOLON }, /* 0x3B ; */ \
|
||||
{1, HID_KEY_COMMA }, /* 0x3C < */ \
|
||||
{0, HID_KEY_EQUAL }, /* 0x3D = */ \
|
||||
{1, HID_KEY_PERIOD }, /* 0x3E > */ \
|
||||
{1, HID_KEY_SLASH }, /* 0x3F ? */ \
|
||||
\
|
||||
{1, HID_KEY_2 }, /* 0x40 @ */ \
|
||||
{1, HID_KEY_A }, /* 0x41 A */ \
|
||||
{1, HID_KEY_B }, /* 0x42 B */ \
|
||||
{1, HID_KEY_C }, /* 0x43 C */ \
|
||||
{1, HID_KEY_D }, /* 0x44 D */ \
|
||||
{1, HID_KEY_E }, /* 0x45 E */ \
|
||||
{1, HID_KEY_F }, /* 0x46 F */ \
|
||||
{1, HID_KEY_G }, /* 0x47 G */ \
|
||||
{1, HID_KEY_H }, /* 0x48 H */ \
|
||||
{1, HID_KEY_I }, /* 0x49 I */ \
|
||||
{1, HID_KEY_J }, /* 0x4A J */ \
|
||||
{1, HID_KEY_K }, /* 0x4B K */ \
|
||||
{1, HID_KEY_L }, /* 0x4C L */ \
|
||||
{1, HID_KEY_M }, /* 0x4D M */ \
|
||||
{1, HID_KEY_N }, /* 0x4E N */ \
|
||||
{1, HID_KEY_O }, /* 0x4F O */ \
|
||||
{1, HID_KEY_P }, /* 0x50 P */ \
|
||||
{1, HID_KEY_Q }, /* 0x51 Q */ \
|
||||
{1, HID_KEY_R }, /* 0x52 R */ \
|
||||
{1, HID_KEY_S }, /* 0x53 S */ \
|
||||
{1, HID_KEY_T }, /* 0x55 T */ \
|
||||
{1, HID_KEY_U }, /* 0x55 U */ \
|
||||
{1, HID_KEY_V }, /* 0x56 V */ \
|
||||
{1, HID_KEY_W }, /* 0x57 W */ \
|
||||
{1, HID_KEY_X }, /* 0x58 X */ \
|
||||
{1, HID_KEY_Y }, /* 0x59 Y */ \
|
||||
{1, HID_KEY_Z }, /* 0x5A Z */ \
|
||||
{0, HID_KEY_BRACKET_LEFT }, /* 0x5B [ */ \
|
||||
{0, HID_KEY_BACKSLASH }, /* 0x5C '\' */ \
|
||||
{0, HID_KEY_BRACKET_RIGHT }, /* 0x5D ] */ \
|
||||
{1, HID_KEY_6 }, /* 0x5E ^ */ \
|
||||
{1, HID_KEY_MINUS }, /* 0x5F _ */ \
|
||||
\
|
||||
{0, HID_KEY_GRAVE }, /* 0x60 ` */ \
|
||||
{0, HID_KEY_A }, /* 0x61 a */ \
|
||||
{0, HID_KEY_B }, /* 0x62 b */ \
|
||||
{0, HID_KEY_C }, /* 0x63 c */ \
|
||||
{0, HID_KEY_D }, /* 0x66 d */ \
|
||||
{0, HID_KEY_E }, /* 0x65 e */ \
|
||||
{0, HID_KEY_F }, /* 0x66 f */ \
|
||||
{0, HID_KEY_G }, /* 0x67 g */ \
|
||||
{0, HID_KEY_H }, /* 0x68 h */ \
|
||||
{0, HID_KEY_I }, /* 0x69 i */ \
|
||||
{0, HID_KEY_J }, /* 0x6A j */ \
|
||||
{0, HID_KEY_K }, /* 0x6B k */ \
|
||||
{0, HID_KEY_L }, /* 0x6C l */ \
|
||||
{0, HID_KEY_M }, /* 0x6D m */ \
|
||||
{0, HID_KEY_N }, /* 0x6E n */ \
|
||||
{0, HID_KEY_O }, /* 0x6F o */ \
|
||||
{0, HID_KEY_P }, /* 0x70 p */ \
|
||||
{0, HID_KEY_Q }, /* 0x71 q */ \
|
||||
{0, HID_KEY_R }, /* 0x72 r */ \
|
||||
{0, HID_KEY_S }, /* 0x73 s */ \
|
||||
{0, HID_KEY_T }, /* 0x75 t */ \
|
||||
{0, HID_KEY_U }, /* 0x75 u */ \
|
||||
{0, HID_KEY_V }, /* 0x76 v */ \
|
||||
{0, HID_KEY_W }, /* 0x77 w */ \
|
||||
{0, HID_KEY_X }, /* 0x78 x */ \
|
||||
{0, HID_KEY_Y }, /* 0x79 y */ \
|
||||
{0, HID_KEY_Z }, /* 0x7A z */ \
|
||||
{1, HID_KEY_BRACKET_LEFT }, /* 0x7B { */ \
|
||||
{1, HID_KEY_BACKSLASH }, /* 0x7C | */ \
|
||||
{1, HID_KEY_BRACKET_RIGHT }, /* 0x7D } */ \
|
||||
{1, HID_KEY_GRAVE }, /* 0x7E ~ */ \
|
||||
{0, HID_KEY_DELETE } /* 0x7F Delete */ \
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* KEYCODE to Ascii Conversion
|
||||
* Expand to array of [128][2] (ascii without shift, ascii with shift)
|
||||
*
|
||||
* Usage: example to convert ascii from keycode (key) and shift modifier (shift).
|
||||
* Here we assume key < 128 ( printable )
|
||||
*
|
||||
* uint8_t const conv_table[128][2] = { HID_KEYCODE_TO_ASCII };
|
||||
* char ch = shift ? conv_table[chr][1] : conv_table[chr][0];
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
#define HID_KEYCODE_TO_ASCII \
|
||||
{0 , 0 }, /* 0x00 */ \
|
||||
{0 , 0 }, /* 0x01 */ \
|
||||
{0 , 0 }, /* 0x02 */ \
|
||||
{0 , 0 }, /* 0x03 */ \
|
||||
{'a' , 'A' }, /* 0x04 */ \
|
||||
{'b' , 'B' }, /* 0x05 */ \
|
||||
{'c' , 'C' }, /* 0x06 */ \
|
||||
{'d' , 'D' }, /* 0x07 */ \
|
||||
{'e' , 'E' }, /* 0x08 */ \
|
||||
{'f' , 'F' }, /* 0x09 */ \
|
||||
{'g' , 'G' }, /* 0x0a */ \
|
||||
{'h' , 'H' }, /* 0x0b */ \
|
||||
{'i' , 'I' }, /* 0x0c */ \
|
||||
{'j' , 'J' }, /* 0x0d */ \
|
||||
{'k' , 'K' }, /* 0x0e */ \
|
||||
{'l' , 'L' }, /* 0x0f */ \
|
||||
{'m' , 'M' }, /* 0x10 */ \
|
||||
{'n' , 'N' }, /* 0x11 */ \
|
||||
{'o' , 'O' }, /* 0x12 */ \
|
||||
{'p' , 'P' }, /* 0x13 */ \
|
||||
{'q' , 'Q' }, /* 0x14 */ \
|
||||
{'r' , 'R' }, /* 0x15 */ \
|
||||
{'s' , 'S' }, /* 0x16 */ \
|
||||
{'t' , 'T' }, /* 0x17 */ \
|
||||
{'u' , 'U' }, /* 0x18 */ \
|
||||
{'v' , 'V' }, /* 0x19 */ \
|
||||
{'w' , 'W' }, /* 0x1a */ \
|
||||
{'x' , 'X' }, /* 0x1b */ \
|
||||
{'y' , 'Y' }, /* 0x1c */ \
|
||||
{'z' , 'Z' }, /* 0x1d */ \
|
||||
{'1' , '!' }, /* 0x1e */ \
|
||||
{'2' , '@' }, /* 0x1f */ \
|
||||
{'3' , '#' }, /* 0x20 */ \
|
||||
{'4' , '$' }, /* 0x21 */ \
|
||||
{'5' , '%' }, /* 0x22 */ \
|
||||
{'6' , '^' }, /* 0x23 */ \
|
||||
{'7' , '&' }, /* 0x24 */ \
|
||||
{'8' , '*' }, /* 0x25 */ \
|
||||
{'9' , '(' }, /* 0x26 */ \
|
||||
{'0' , ')' }, /* 0x27 */ \
|
||||
{'\r' , '\r' }, /* 0x28 */ \
|
||||
{'\x1b', '\x1b' }, /* 0x29 */ \
|
||||
{'\b' , '\b' }, /* 0x2a */ \
|
||||
{'\t' , '\t' }, /* 0x2b */ \
|
||||
{' ' , ' ' }, /* 0x2c */ \
|
||||
{'-' , '_' }, /* 0x2d */ \
|
||||
{'=' , '+' }, /* 0x2e */ \
|
||||
{'[' , '{' }, /* 0x2f */ \
|
||||
{']' , '}' }, /* 0x30 */ \
|
||||
{'\\' , '|' }, /* 0x31 */ \
|
||||
{'#' , '~' }, /* 0x32 */ \
|
||||
{';' , ':' }, /* 0x33 */ \
|
||||
{'\'' , '\"' }, /* 0x34 */ \
|
||||
{0 , 0 }, /* 0x35 */ \
|
||||
{',' , '<' }, /* 0x36 */ \
|
||||
{'.' , '>' }, /* 0x37 */ \
|
||||
{'/' , '?' }, /* 0x38 */ \
|
||||
\
|
||||
{0 , 0 }, /* 0x39 */ \
|
||||
{0 , 0 }, /* 0x3a */ \
|
||||
{0 , 0 }, /* 0x3b */ \
|
||||
{0 , 0 }, /* 0x3c */ \
|
||||
{0 , 0 }, /* 0x3d */ \
|
||||
{0 , 0 }, /* 0x3e */ \
|
||||
{0 , 0 }, /* 0x3f */ \
|
||||
{0 , 0 }, /* 0x40 */ \
|
||||
{0 , 0 }, /* 0x41 */ \
|
||||
{0 , 0 }, /* 0x42 */ \
|
||||
{0 , 0 }, /* 0x43 */ \
|
||||
{0 , 0 }, /* 0x44 */ \
|
||||
{0 , 0 }, /* 0x45 */ \
|
||||
{0 , 0 }, /* 0x46 */ \
|
||||
{0 , 0 }, /* 0x47 */ \
|
||||
{0 , 0 }, /* 0x48 */ \
|
||||
{0 , 0 }, /* 0x49 */ \
|
||||
{0 , 0 }, /* 0x4a */ \
|
||||
{0 , 0 }, /* 0x4b */ \
|
||||
{0 , 0 }, /* 0x4c */ \
|
||||
{0 , 0 }, /* 0x4d */ \
|
||||
{0 , 0 }, /* 0x4e */ \
|
||||
{0 , 0 }, /* 0x4f */ \
|
||||
{0 , 0 }, /* 0x50 */ \
|
||||
{0 , 0 }, /* 0x51 */ \
|
||||
{0 , 0 }, /* 0x52 */ \
|
||||
{0 , 0 }, /* 0x53 */ \
|
||||
\
|
||||
{'/' , '/' }, /* 0x54 */ \
|
||||
{'*' , '*' }, /* 0x55 */ \
|
||||
{'-' , '-' }, /* 0x56 */ \
|
||||
{'+' , '+' }, /* 0x57 */ \
|
||||
{'\r' , '\r' }, /* 0x58 */ \
|
||||
{'1' , 0 }, /* 0x59 */ \
|
||||
{'2' , 0 }, /* 0x5a */ \
|
||||
{'3' , 0 }, /* 0x5b */ \
|
||||
{'4' , 0 }, /* 0x5c */ \
|
||||
{'5' , '5' }, /* 0x5d */ \
|
||||
{'6' , 0 }, /* 0x5e */ \
|
||||
{'7' , 0 }, /* 0x5f */ \
|
||||
{'8' , 0 }, /* 0x60 */ \
|
||||
{'9' , 0 }, /* 0x61 */ \
|
||||
{'0' , 0 }, /* 0x62 */ \
|
||||
{'0' , 0 }, /* 0x63 */ \
|
||||
{'=' , '=' }, /* 0x67 */ \
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -73,28 +73,31 @@ bool tud_hid_ready(void)
|
||||
{
|
||||
uint8_t itf = 0;
|
||||
uint8_t const ep_in = _hidd_itf[itf].ep_in;
|
||||
return tud_ready() && (ep_in != 0) && !dcd_edpt_busy(TUD_OPT_RHPORT, ep_in);
|
||||
return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(TUD_OPT_RHPORT, ep_in);
|
||||
}
|
||||
|
||||
bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
|
||||
{
|
||||
TU_VERIFY( tud_hid_ready() && (len <= CFG_TUD_HID_BUFSIZE) );
|
||||
TU_VERIFY( tud_hid_ready() );
|
||||
|
||||
uint8_t itf = 0;
|
||||
hidd_interface_t * p_hid = &_hidd_itf[itf];
|
||||
|
||||
// If report id = 0, skip ID field
|
||||
if (report_id)
|
||||
{
|
||||
len = tu_min8(len, CFG_TUD_HID_BUFSIZE-1);
|
||||
|
||||
p_hid->epin_buf[0] = report_id;
|
||||
memcpy(p_hid->epin_buf+1, report, len);
|
||||
len++;
|
||||
}else
|
||||
{
|
||||
// If report id = 0, skip ID field
|
||||
len = tu_min8(len, CFG_TUD_HID_BUFSIZE);
|
||||
memcpy(p_hid->epin_buf, report, len);
|
||||
}
|
||||
|
||||
return dcd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len);
|
||||
return usbd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len);
|
||||
}
|
||||
|
||||
bool tud_hid_boot_mode(void)
|
||||
@ -180,7 +183,7 @@ bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t
|
||||
*p_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
|
||||
|
||||
// Prepare for output endpoint
|
||||
if (p_hid->ep_out) TU_ASSERT(dcd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
|
||||
if (p_hid->ep_out) TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -303,7 +306,7 @@ bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
|
||||
if (ep_addr == p_hid->ep_out)
|
||||
{
|
||||
tud_hid_set_report_cb(0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes);
|
||||
TU_ASSERT(dcd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -82,12 +82,12 @@ uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type,
|
||||
void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
|
||||
|
||||
// Invoked when received SET_PROTOCOL request ( mode switch Boot <-> Report )
|
||||
ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t boot_mode);
|
||||
TU_ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t boot_mode);
|
||||
|
||||
// Invoked when received SET_IDLE request. return false will stall the request
|
||||
// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
|
||||
// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
|
||||
ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
|
||||
TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
|
||||
|
||||
/* --------------------------------------------------------------------+
|
||||
* HID Report Descriptor Template
|
||||
@ -297,155 +297,6 @@ ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
|
||||
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END \
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* ASCII to KEYCODE Conversion
|
||||
* Expand to array of [128][2] (shift, keycode)
|
||||
*
|
||||
* Usage: example to convert input char into keyboard report (modifier + keycode)
|
||||
*
|
||||
* uint8_t const conv_table[128][2] = { HID_ASCII_TO_KEYCODE };
|
||||
*
|
||||
* uint8_t keycode[6] = { 0 };
|
||||
* uint8_t modifier = 0;
|
||||
*
|
||||
* if ( conv_table[chr][0] ) modifier = KEYBOARD_MODIFIER_LEFTSHIFT;
|
||||
* keycode[0] = conv_table[chr][1];
|
||||
* tud_hid_keyboard_report(report_id, modifier, keycode);
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
#define HID_ASCII_TO_KEYCODE \
|
||||
{0, 0 }, /* 0x00 Null */ \
|
||||
{0, 0 }, /* 0x01 */ \
|
||||
{0, 0 }, /* 0x02 */ \
|
||||
{0, 0 }, /* 0x03 */ \
|
||||
{0, 0 }, /* 0x04 */ \
|
||||
{0, 0 }, /* 0x05 */ \
|
||||
{0, 0 }, /* 0x06 */ \
|
||||
{0, 0 }, /* 0x07 */ \
|
||||
{0, HID_KEY_BACKSPACE }, /* 0x08 Backspace */ \
|
||||
{0, HID_KEY_TAB }, /* 0x09 Tab */ \
|
||||
{0, HID_KEY_RETURN }, /* 0x0A Line Feed */ \
|
||||
{0, 0 }, /* 0x0B */ \
|
||||
{0, 0 }, /* 0x0C */ \
|
||||
{0, HID_KEY_RETURN }, /* 0x0D CR */ \
|
||||
{0, 0 }, /* 0x0E */ \
|
||||
{0, 0 }, /* 0x0F */ \
|
||||
{0, 0 }, /* 0x10 */ \
|
||||
{0, 0 }, /* 0x11 */ \
|
||||
{0, 0 }, /* 0x12 */ \
|
||||
{0, 0 }, /* 0x13 */ \
|
||||
{0, 0 }, /* 0x14 */ \
|
||||
{0, 0 }, /* 0x15 */ \
|
||||
{0, 0 }, /* 0x16 */ \
|
||||
{0, 0 }, /* 0x17 */ \
|
||||
{0, 0 }, /* 0x18 */ \
|
||||
{0, 0 }, /* 0x19 */ \
|
||||
{0, 0 }, /* 0x1A */ \
|
||||
{0, HID_KEY_ESCAPE }, /* 0x1B Escape */ \
|
||||
{0, 0 }, /* 0x1C */ \
|
||||
{0, 0 }, /* 0x1D */ \
|
||||
{0, 0 }, /* 0x1E */ \
|
||||
{0, 0 }, /* 0x1F */ \
|
||||
\
|
||||
{0, HID_KEY_SPACE }, /* 0x20 */ \
|
||||
{1, HID_KEY_1 }, /* 0x21 ! */ \
|
||||
{1, HID_KEY_APOSTROPHE }, /* 0x22 " */ \
|
||||
{1, HID_KEY_3 }, /* 0x23 # */ \
|
||||
{1, HID_KEY_4 }, /* 0x24 $ */ \
|
||||
{1, HID_KEY_5 }, /* 0x25 % */ \
|
||||
{1, HID_KEY_7 }, /* 0x26 & */ \
|
||||
{0, HID_KEY_APOSTROPHE }, /* 0x27 ' */ \
|
||||
{1, HID_KEY_9 }, /* 0x28 ( */ \
|
||||
{1, HID_KEY_0 }, /* 0x29 ) */ \
|
||||
{1, HID_KEY_8 }, /* 0x2A * */ \
|
||||
{1, HID_KEY_EQUAL }, /* 0x2B + */ \
|
||||
{0, HID_KEY_COMMA }, /* 0x2C , */ \
|
||||
{0, HID_KEY_MINUS }, /* 0x2D - */ \
|
||||
{0, HID_KEY_PERIOD }, /* 0x2E . */ \
|
||||
{0, HID_KEY_SLASH }, /* 0x2F / */ \
|
||||
{0, HID_KEY_0 }, /* 0x30 0 */ \
|
||||
{0, HID_KEY_1 }, /* 0x31 1 */ \
|
||||
{0, HID_KEY_2 }, /* 0x32 2 */ \
|
||||
{0, HID_KEY_3 }, /* 0x33 3 */ \
|
||||
{0, HID_KEY_4 }, /* 0x34 4 */ \
|
||||
{0, HID_KEY_5 }, /* 0x35 5 */ \
|
||||
{0, HID_KEY_6 }, /* 0x36 6 */ \
|
||||
{0, HID_KEY_7 }, /* 0x37 7 */ \
|
||||
{0, HID_KEY_8 }, /* 0x38 8 */ \
|
||||
{0, HID_KEY_9 }, /* 0x39 9 */ \
|
||||
{1, HID_KEY_SEMICOLON }, /* 0x3A : */ \
|
||||
{0, HID_KEY_SEMICOLON }, /* 0x3B ; */ \
|
||||
{1, HID_KEY_COMMA }, /* 0x3C < */ \
|
||||
{0, HID_KEY_EQUAL }, /* 0x3D = */ \
|
||||
{1, HID_KEY_PERIOD }, /* 0x3E > */ \
|
||||
{1, HID_KEY_SLASH }, /* 0x3F ? */ \
|
||||
\
|
||||
{1, HID_KEY_2 }, /* 0x40 @ */ \
|
||||
{1, HID_KEY_A }, /* 0x41 A */ \
|
||||
{1, HID_KEY_B }, /* 0x42 B */ \
|
||||
{1, HID_KEY_C }, /* 0x43 C */ \
|
||||
{1, HID_KEY_D }, /* 0x44 D */ \
|
||||
{1, HID_KEY_E }, /* 0x45 E */ \
|
||||
{1, HID_KEY_F }, /* 0x46 F */ \
|
||||
{1, HID_KEY_G }, /* 0x47 G */ \
|
||||
{1, HID_KEY_H }, /* 0x48 H */ \
|
||||
{1, HID_KEY_I }, /* 0x49 I */ \
|
||||
{1, HID_KEY_J }, /* 0x4A J */ \
|
||||
{1, HID_KEY_K }, /* 0x4B K */ \
|
||||
{1, HID_KEY_L }, /* 0x4C L */ \
|
||||
{1, HID_KEY_M }, /* 0x4D M */ \
|
||||
{1, HID_KEY_N }, /* 0x4E N */ \
|
||||
{1, HID_KEY_O }, /* 0x4F O */ \
|
||||
{1, HID_KEY_P }, /* 0x50 P */ \
|
||||
{1, HID_KEY_Q }, /* 0x51 Q */ \
|
||||
{1, HID_KEY_R }, /* 0x52 R */ \
|
||||
{1, HID_KEY_S }, /* 0x53 S */ \
|
||||
{1, HID_KEY_T }, /* 0x55 T */ \
|
||||
{1, HID_KEY_U }, /* 0x55 U */ \
|
||||
{1, HID_KEY_V }, /* 0x56 V */ \
|
||||
{1, HID_KEY_W }, /* 0x57 W */ \
|
||||
{1, HID_KEY_X }, /* 0x58 X */ \
|
||||
{1, HID_KEY_Y }, /* 0x59 Y */ \
|
||||
{1, HID_KEY_Z }, /* 0x5A Z */ \
|
||||
{0, HID_KEY_BRACKET_LEFT }, /* 0x5B [ */ \
|
||||
{0, HID_KEY_BACKSLASH }, /* 0x5C '\' */ \
|
||||
{0, HID_KEY_BRACKET_RIGHT }, /* 0x5D ] */ \
|
||||
{1, HID_KEY_6 }, /* 0x5E ^ */ \
|
||||
{1, HID_KEY_MINUS }, /* 0x5F _ */ \
|
||||
\
|
||||
{0, HID_KEY_GRAVE }, /* 0x60 ` */ \
|
||||
{0, HID_KEY_A }, /* 0x61 a */ \
|
||||
{0, HID_KEY_B }, /* 0x62 b */ \
|
||||
{0, HID_KEY_C }, /* 0x63 c */ \
|
||||
{0, HID_KEY_D }, /* 0x66 d */ \
|
||||
{0, HID_KEY_E }, /* 0x65 e */ \
|
||||
{0, HID_KEY_F }, /* 0x66 f */ \
|
||||
{0, HID_KEY_G }, /* 0x67 g */ \
|
||||
{0, HID_KEY_H }, /* 0x68 h */ \
|
||||
{0, HID_KEY_I }, /* 0x69 i */ \
|
||||
{0, HID_KEY_J }, /* 0x6A j */ \
|
||||
{0, HID_KEY_K }, /* 0x6B k */ \
|
||||
{0, HID_KEY_L }, /* 0x6C l */ \
|
||||
{0, HID_KEY_M }, /* 0x6D m */ \
|
||||
{0, HID_KEY_N }, /* 0x6E n */ \
|
||||
{0, HID_KEY_O }, /* 0x6F o */ \
|
||||
{0, HID_KEY_P }, /* 0x70 p */ \
|
||||
{0, HID_KEY_Q }, /* 0x71 q */ \
|
||||
{0, HID_KEY_R }, /* 0x72 r */ \
|
||||
{0, HID_KEY_S }, /* 0x73 s */ \
|
||||
{0, HID_KEY_T }, /* 0x75 t */ \
|
||||
{0, HID_KEY_U }, /* 0x75 u */ \
|
||||
{0, HID_KEY_V }, /* 0x76 v */ \
|
||||
{0, HID_KEY_W }, /* 0x77 w */ \
|
||||
{0, HID_KEY_X }, /* 0x78 x */ \
|
||||
{0, HID_KEY_Y }, /* 0x79 y */ \
|
||||
{0, HID_KEY_Z }, /* 0x7A z */ \
|
||||
{1, HID_KEY_BRACKET_LEFT }, /* 0x7B { */ \
|
||||
{1, HID_KEY_BACKSLASH }, /* 0x7C | */ \
|
||||
{1, HID_KEY_BRACKET_RIGHT }, /* 0x7D } */ \
|
||||
{1, HID_KEY_GRAVE }, /* 0x7E ~ */ \
|
||||
{0, HID_KEY_DELETE } /* 0x7F Delete */ \
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -42,7 +42,7 @@
|
||||
// FUNCTIONAL DESCRIPTOR (COMMUNICATION INTERFACE)
|
||||
//--------------------------------------------------------------------+
|
||||
/// Header Functional Descriptor (Communication Interface)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
@ -52,7 +52,7 @@ typedef struct ATTR_PACKED
|
||||
}midi_desc_func_header_t;
|
||||
|
||||
/// Union Functional Descriptor (Communication Interface)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
|
@ -72,7 +72,7 @@ typedef struct
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
CFG_TUSB_ATTR_USBRAM midid_interface_t _midid_itf[CFG_TUD_MIDI];
|
||||
CFG_TUSB_MEM_SECTION midid_interface_t _midid_itf[CFG_TUD_MIDI];
|
||||
|
||||
bool tud_midi_n_connected(uint8_t itf) {
|
||||
midid_interface_t* midi = &_midid_itf[itf];
|
||||
@ -133,13 +133,13 @@ void midi_rx_done_cb(midid_interface_t* midi, uint8_t const* buffer, uint32_t bu
|
||||
|
||||
static bool maybe_transmit(midid_interface_t* midi, uint8_t itf_index)
|
||||
{
|
||||
TU_VERIFY( !dcd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) ); // skip if previous transfer not complete
|
||||
TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) ); // skip if previous transfer not complete
|
||||
|
||||
uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EPSIZE);
|
||||
if (count > 0)
|
||||
{
|
||||
TU_VERIFY( tud_midi_n_connected(itf_index) ); // fifo is empty if not connected
|
||||
TU_ASSERT( dcd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) );
|
||||
TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -304,7 +304,7 @@ bool midid_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc,
|
||||
}
|
||||
|
||||
// Prepare for incoming data
|
||||
TU_ASSERT( dcd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false);
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -334,7 +334,7 @@ bool midid_xfer_cb(uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint
|
||||
midi_rx_done_cb(p_midi, p_midi->epout_buf, xferred_bytes);
|
||||
|
||||
// prepare for next
|
||||
TU_ASSERT( dcd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false );
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false );
|
||||
} else if ( edpt_addr == p_midi->ep_in ) {
|
||||
maybe_transmit(p_midi, itf);
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ static inline bool tud_midi_write_flush (void)
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION CALLBACK API (WEAK is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
ATTR_WEAK void tud_midi_rx_cb(uint8_t itf);
|
||||
TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
|
@ -86,7 +86,7 @@ typedef enum
|
||||
}msc_csw_status_t;
|
||||
|
||||
/// Command Block Wrapper
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint32_t signature; ///< Signature that helps identify this data packet as a CBW. The signature field shall contain the value 43425355h (little endian), indicating a CBW.
|
||||
uint32_t tag; ///< Tag sent by the host. The device shall echo the contents of this field back to the host in the dCSWTagfield of the associated CSW. The dCSWTagpositively associates a CSW with the corresponding CBW.
|
||||
@ -100,7 +100,7 @@ typedef struct ATTR_PACKED
|
||||
TU_VERIFY_STATIC(sizeof(msc_cbw_t) == 31, "size is not correct");
|
||||
|
||||
/// Command Status Wrapper
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint32_t signature ; ///< Signature that helps identify this data packet as a CSW. The signature field shall contain the value 53425355h (little endian), indicating CSW.
|
||||
uint32_t tag ; ///< The device shall set this field to the value received in the dCBWTag of the associated CBW.
|
||||
@ -153,7 +153,7 @@ typedef enum
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// SCSI Test Unit Ready Command
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_TEST_UNIT_READY
|
||||
uint8_t lun ; ///< Logical Unit
|
||||
@ -164,7 +164,7 @@ typedef struct ATTR_PACKED
|
||||
TU_VERIFY_STATIC(sizeof(scsi_test_unit_ready_t) == 6, "size is not correct");
|
||||
|
||||
/// SCSI Inquiry Command
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_INQUIRY
|
||||
uint8_t reserved1 ;
|
||||
@ -177,7 +177,7 @@ typedef struct ATTR_PACKED
|
||||
TU_VERIFY_STATIC(sizeof(scsi_inquiry_t) == 6, "size is not correct");
|
||||
|
||||
/// SCSI Inquiry Response Data
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t peripheral_device_type : 5;
|
||||
uint8_t peripheral_qualifier : 3;
|
||||
@ -223,7 +223,7 @@ typedef struct ATTR_PACKED
|
||||
TU_VERIFY_STATIC(sizeof(scsi_inquiry_resp_t) == 36, "size is not correct");
|
||||
|
||||
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t response_code : 7; ///< 70h - current errors, Fixed Format 71h - deferred errors, Fixed Format
|
||||
uint8_t valid : 1;
|
||||
@ -249,7 +249,7 @@ typedef struct ATTR_PACKED
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(scsi_sense_fixed_resp_t) == 18, "size is not correct");
|
||||
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_MODE_SENSE_6
|
||||
|
||||
@ -268,7 +268,7 @@ typedef struct ATTR_PACKED
|
||||
TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_t) == 6, "size is not correct");
|
||||
|
||||
// This is only a Mode parameter header(6).
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t data_len;
|
||||
uint8_t medium_type;
|
||||
@ -281,7 +281,7 @@ typedef struct ATTR_PACKED
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_resp_t) == 4, "size is not correct");
|
||||
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
|
||||
uint8_t reserved[3];
|
||||
@ -291,7 +291,7 @@ typedef struct ATTR_PACKED
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(scsi_prevent_allow_medium_removal_t) == 6, "size is not correct");
|
||||
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code;
|
||||
|
||||
@ -318,7 +318,7 @@ TU_VERIFY_STATIC( sizeof(scsi_start_stop_unit_t) == 6, "size is not correct");
|
||||
// SCSI MMC
|
||||
//--------------------------------------------------------------------+
|
||||
/// SCSI Read Format Capacity: Write Capacity
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code;
|
||||
uint8_t reserved[6];
|
||||
@ -328,7 +328,7 @@ typedef struct ATTR_PACKED
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_t) == 10, "size is not correct");
|
||||
|
||||
typedef struct ATTR_PACKED{
|
||||
typedef struct TU_ATTR_PACKED{
|
||||
uint8_t reserved[3];
|
||||
uint8_t list_length; /// must be 8*n, length in bytes of formattable capacity descriptor followed it.
|
||||
|
||||
@ -348,7 +348,7 @@ TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_data_t) == 12, "size is not c
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// SCSI Read Capacity 10 Command: Read Capacity
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_READ_CAPACITY_10
|
||||
uint8_t reserved1 ;
|
||||
@ -369,7 +369,7 @@ typedef struct {
|
||||
TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_resp_t) == 8, "size is not correct");
|
||||
|
||||
/// SCSI Read 10 Command
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code ; ///< SCSI OpCode
|
||||
uint8_t reserved ; // has LUN according to wiki
|
||||
|
@ -139,7 +139,7 @@ bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
|
||||
(*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
|
||||
|
||||
// Prepare for Command Block Wrapper
|
||||
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -394,7 +394,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
if ( (p_cbw->total_bytes > 0 ) && !tu_bit_test(p_cbw->dir, 7) )
|
||||
{
|
||||
// queue transfer
|
||||
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
|
||||
}else
|
||||
{
|
||||
int32_t resplen;
|
||||
@ -428,7 +428,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
if (p_msc->total_len)
|
||||
{
|
||||
TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect
|
||||
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
|
||||
}else
|
||||
{
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
@ -543,7 +543,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
p_msc->stage = MSC_STAGE_CMD;
|
||||
|
||||
// Send SCSI Status
|
||||
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)) );
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)) );
|
||||
|
||||
// Invoke complete callback if defined
|
||||
if ( SCSI_CMD_READ_10 == p_cbw->command[0])
|
||||
@ -560,7 +560,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
|
||||
}
|
||||
|
||||
// Queue for the next CBW
|
||||
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,7 +602,7 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
|
||||
}
|
||||
else
|
||||
{
|
||||
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), );
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), );
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,7 +627,7 @@ static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
|
||||
int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
|
||||
|
||||
// Write10 callback will be called later when usb transfer complete
|
||||
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -126,24 +126,24 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
|
||||
/*------------- Optional callbacks -------------*/
|
||||
|
||||
// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
|
||||
ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
|
||||
TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
|
||||
|
||||
// Invoked when received Start Stop Unit command
|
||||
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
|
||||
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||
ATTR_WEAK void tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
|
||||
TU_ATTR_WEAK void tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
|
||||
|
||||
// Invoked when Read10 command is complete
|
||||
ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun);
|
||||
TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun);
|
||||
|
||||
// Invoke when Write10 command is complete, can be used to flush flash caching
|
||||
ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun);
|
||||
TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun);
|
||||
|
||||
// Invoked when command in tud_msc_scsi_cb is complete
|
||||
ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]);
|
||||
TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]);
|
||||
|
||||
// Hook to make a mass storage device read-only. TODO remove
|
||||
ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
|
||||
TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
@ -44,7 +44,7 @@ static osal_semaphore_def_t msch_sem_def;
|
||||
static osal_semaphore_t msch_sem_hdl;
|
||||
|
||||
// buffer used to read scsi information when mounted, largest response data currently is inquiry
|
||||
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(4) static uint8_t msch_buffer[sizeof(scsi_inquiry_resp_t)];
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msch_buffer[sizeof(scsi_inquiry_resp_t)];
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
|
@ -57,9 +57,6 @@
|
||||
|
||||
#define TU_BIT(n) (1U << (n))
|
||||
|
||||
// for declaration of reserved field, make use of _TU_COUNTER_
|
||||
#define TU_RESERVED XSTRING_CONCAT_(reserved, _TU_COUNTER_)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDES
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -32,10 +32,10 @@
|
||||
#ifndef _TUSB_COMPILER_H_
|
||||
#define _TUSB_COMPILER_H_
|
||||
|
||||
#define STRING_(x) #x ///< stringify without expand
|
||||
#define XSTRING_(x) STRING_(x) ///< expand then stringify
|
||||
#define STRING_CONCAT_(a, b) a##b ///< concat without expand
|
||||
#define XSTRING_CONCAT_(a, b) STRING_CONCAT_(a, b) ///< expand then concat
|
||||
#define TU_STRING(x) #x ///< stringify without expand
|
||||
#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify
|
||||
#define TU_STRCAT(a, b) a##b ///< concat without expand
|
||||
#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat
|
||||
|
||||
#if defined __COUNTER__ && __COUNTER__ != __COUNTER__
|
||||
#define _TU_COUNTER_ __COUNTER__
|
||||
@ -47,20 +47,23 @@
|
||||
#if __STDC_VERSION__ >= 201112L
|
||||
#define TU_VERIFY_STATIC _Static_assert
|
||||
#else
|
||||
#define TU_VERIFY_STATIC(const_expr, _mess) enum { XSTRING_CONCAT_(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
|
||||
#define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
|
||||
#endif
|
||||
|
||||
// for declaration of reserved field, make use of _TU_COUNTER_
|
||||
#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Compiler porting with Attribute and Endian
|
||||
//--------------------------------------------------------------------+
|
||||
#if defined(__GNUC__)
|
||||
#define ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
|
||||
#define ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
|
||||
#define ATTR_PACKED __attribute__ ((packed))
|
||||
#define ATTR_PREPACKED
|
||||
#define ATTR_WEAK __attribute__ ((weak))
|
||||
#define ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
|
||||
#define ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
|
||||
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
|
||||
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
|
||||
#define TU_ATTR_PACKED __attribute__ ((packed))
|
||||
#define TU_ATTR_PREPACKED
|
||||
#define TU_ATTR_WEAK __attribute__ ((weak))
|
||||
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
|
||||
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
|
||||
|
||||
// Endian conversion use well-known host to network (big endian) naming
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
|
@ -71,6 +71,35 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
|
||||
return true;
|
||||
}
|
||||
|
||||
// retrieve data from fifo
|
||||
static void _tu_ff_pull(tu_fifo_t* f, void * buffer)
|
||||
{
|
||||
memcpy(buffer,
|
||||
f->buffer + (f->rd_idx * f->item_size),
|
||||
f->item_size);
|
||||
|
||||
f->rd_idx = (f->rd_idx + 1) % f->depth;
|
||||
f->count--;
|
||||
}
|
||||
|
||||
// send data to fifo
|
||||
static void _tu_ff_push(tu_fifo_t* f, void const * data)
|
||||
{
|
||||
memcpy( f->buffer + (f->wr_idx * f->item_size),
|
||||
data,
|
||||
f->item_size);
|
||||
|
||||
f->wr_idx = (f->wr_idx + 1) % f->depth;
|
||||
|
||||
if (tu_fifo_full(f))
|
||||
{
|
||||
f->rd_idx = f->wr_idx; // keep the full state (rd == wr && len = size)
|
||||
}
|
||||
else
|
||||
{
|
||||
f->count++;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*!
|
||||
@ -82,23 +111,19 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
|
||||
|
||||
@param[in] f
|
||||
Pointer to the FIFO buffer to manipulate
|
||||
@param[in] p_buffer
|
||||
@param[in] buffer
|
||||
Pointer to the place holder for data read from the buffer
|
||||
|
||||
@returns TRUE if the queue is not empty
|
||||
*/
|
||||
/******************************************************************************/
|
||||
bool tu_fifo_read(tu_fifo_t* f, void * p_buffer)
|
||||
bool tu_fifo_read(tu_fifo_t* f, void * buffer)
|
||||
{
|
||||
if( tu_fifo_empty(f) ) return false;
|
||||
|
||||
tu_fifo_lock(f);
|
||||
|
||||
memcpy(p_buffer,
|
||||
f->buffer + (f->rd_idx * f->item_size),
|
||||
f->item_size);
|
||||
f->rd_idx = (f->rd_idx + 1) % f->depth;
|
||||
f->count--;
|
||||
_tu_ff_pull(f, buffer);
|
||||
|
||||
tu_fifo_unlock(f);
|
||||
|
||||
@ -113,7 +138,7 @@ bool tu_fifo_read(tu_fifo_t* f, void * p_buffer)
|
||||
|
||||
@param[in] f
|
||||
Pointer to the FIFO buffer to manipulate
|
||||
@param[in] p_data
|
||||
@param[in] buffer
|
||||
The pointer to data location
|
||||
@param[in] count
|
||||
Number of element that buffer can afford
|
||||
@ -121,27 +146,28 @@ bool tu_fifo_read(tu_fifo_t* f, void * p_buffer)
|
||||
@returns number of items read from the FIFO
|
||||
*/
|
||||
/******************************************************************************/
|
||||
uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t count)
|
||||
uint16_t tu_fifo_read_n (tu_fifo_t* f, void * buffer, uint16_t count)
|
||||
{
|
||||
if( tu_fifo_empty(f) ) return 0;
|
||||
|
||||
tu_fifo_lock(f);
|
||||
|
||||
/* Limit up to fifo's count */
|
||||
if ( count > f->count ) count = f->count;
|
||||
|
||||
/* Could copy up to 2 portions marked as 'x' if queue is wrapped around
|
||||
* case 1: ....RxxxxW.......
|
||||
* case 2: xxxxxW....Rxxxxxx
|
||||
*/
|
||||
// uint16_t index2upper = tu_min16(count, f->count-f->rd_idx);
|
||||
|
||||
uint8_t* p_buf = (uint8_t*) p_buffer;
|
||||
uint8_t* buf8 = (uint8_t*) buffer;
|
||||
uint16_t len = 0;
|
||||
while( (len < count) && tu_fifo_read(f, p_buf) )
|
||||
|
||||
while (len < count)
|
||||
{
|
||||
_tu_ff_pull(f, buf8);
|
||||
|
||||
len++;
|
||||
p_buf += f->item_size;
|
||||
buf8 += f->item_size;
|
||||
}
|
||||
|
||||
tu_fifo_unlock(f);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -182,33 +208,20 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer)
|
||||
|
||||
@param[in] f
|
||||
Pointer to the FIFO buffer to manipulate
|
||||
@param[in] p_data
|
||||
@param[in] data
|
||||
The byte to add to the FIFO
|
||||
|
||||
@returns TRUE if the data was written to the FIFO (overwrittable
|
||||
FIFO will always return TRUE)
|
||||
*/
|
||||
/******************************************************************************/
|
||||
bool tu_fifo_write (tu_fifo_t* f, const void * p_data)
|
||||
bool tu_fifo_write (tu_fifo_t* f, const void * data)
|
||||
{
|
||||
if ( tu_fifo_full(f) && !f->overwritable ) return false;
|
||||
|
||||
tu_fifo_lock(f);
|
||||
|
||||
memcpy( f->buffer + (f->wr_idx * f->item_size),
|
||||
p_data,
|
||||
f->item_size);
|
||||
|
||||
f->wr_idx = (f->wr_idx + 1) % f->depth;
|
||||
|
||||
if (tu_fifo_full(f))
|
||||
{
|
||||
f->rd_idx = f->wr_idx; // keep the full state (rd == wr && len = size)
|
||||
}
|
||||
else
|
||||
{
|
||||
f->count++;
|
||||
}
|
||||
_tu_ff_push(f, data);
|
||||
|
||||
tu_fifo_unlock(f);
|
||||
|
||||
@ -223,26 +236,35 @@ bool tu_fifo_write (tu_fifo_t* f, const void * p_data)
|
||||
|
||||
@param[in] f
|
||||
Pointer to the FIFO buffer to manipulate
|
||||
@param[in] p_data
|
||||
@param[in] data
|
||||
The pointer to data to add to the FIFO
|
||||
@param[in] count
|
||||
Number of element
|
||||
@return Number of written elements
|
||||
*/
|
||||
/******************************************************************************/
|
||||
uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * p_data, uint16_t count)
|
||||
uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * data, uint16_t count)
|
||||
{
|
||||
if ( count == 0 ) return 0;
|
||||
|
||||
uint8_t const* p_buf = (uint8_t const*) p_data;
|
||||
tu_fifo_lock(f);
|
||||
|
||||
// Not overwritable limit up to full
|
||||
if (!f->overwritable) count = tu_min16(count, tu_fifo_remaining(f));
|
||||
|
||||
uint8_t const* buf8 = (uint8_t const*) data;
|
||||
uint16_t len = 0;
|
||||
while( (len < count) && tu_fifo_write(f, p_buf) )
|
||||
|
||||
while (len < count)
|
||||
{
|
||||
_tu_ff_push(f, buf8);
|
||||
|
||||
len++;
|
||||
p_buf += f->item_size;
|
||||
buf8 += f->item_size;
|
||||
}
|
||||
|
||||
tu_fifo_unlock(f);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -200,7 +200,7 @@ enum
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// USB Standard Device Descriptor (section 9.6.1, table 9-8)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
|
||||
@ -222,7 +222,7 @@ typedef struct ATTR_PACKED
|
||||
} tusb_desc_device_t;
|
||||
|
||||
/// USB Standard Configuration Descriptor (section 9.6.1 table 9-10) */
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
|
||||
@ -236,7 +236,7 @@ typedef struct ATTR_PACKED
|
||||
} tusb_desc_configuration_t;
|
||||
|
||||
/// USB Standard Interface Descriptor (section 9.6.1 table 9-12)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type
|
||||
@ -251,21 +251,21 @@ typedef struct ATTR_PACKED
|
||||
} tusb_desc_interface_t;
|
||||
|
||||
/// USB Standard Endpoint Descriptor (section 9.6.1 table 9-13)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< ENDPOINT Descriptor Type
|
||||
|
||||
uint8_t bEndpointAddress ; ///< The address of the endpoint on the USB device described by this descriptor. The address is encoded as follows: \n Bit 3...0: The endpoint number \n Bit 6...4: Reserved, reset to zero \n Bit 7: Direction, ignored for control endpoints 0 = OUT endpoint 1 = IN endpoint.
|
||||
|
||||
struct ATTR_PACKED {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t xfer : 2;
|
||||
uint8_t sync : 2;
|
||||
uint8_t usage : 2;
|
||||
uint8_t : 2;
|
||||
} bmAttributes ; ///< This field describes the endpoint's attributes when it is configured using the bConfigurationValue. \n Bits 1..0: Transfer Type \n- 00 = Control \n- 01 = Isochronous \n- 10 = Bulk \n- 11 = Interrupt \n If not an isochronous endpoint, bits 5..2 are reserved and must be set to zero. If isochronous, they are defined as follows: \n Bits 3..2: Synchronization Type \n- 00 = No Synchronization \n- 01 = Asynchronous \n- 10 = Adaptive \n- 11 = Synchronous \n Bits 5..4: Usage Type \n- 00 = Data endpoint \n- 01 = Feedback endpoint \n- 10 = Implicit feedback Data endpoint \n- 11 = Reserved \n Refer to Chapter 5 of USB 2.0 specification for more information. \n All other bits are reserved and must be reset to zero. Reserved bits must be ignored by the host.
|
||||
|
||||
struct ATTR_PACKED {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint16_t size : 11; ///< Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected. \n For isochronous endpoints, this value is used to reserve the bus time in the schedule, required for the per-(micro)frame data payloads. The pipe may, on an ongoing basis, actually use less bandwidth than that reserved. The device reports, if necessary, the actual bandwidth used via its normal, non-USB defined mechanisms. \n For all endpoints, bits 10..0 specify the maximum packet size (in bytes). \n For high-speed isochronous and interrupt endpoints: \n Bits 12..11 specify the number of additional transaction opportunities per microframe: \n- 00 = None (1 transaction per microframe) \n- 01 = 1 additional (2 per microframe) \n- 10 = 2 additional (3 per microframe) \n- 11 = Reserved \n Bits 15..13 are reserved and must be set to zero.
|
||||
uint16_t hs_period_mult : 2;
|
||||
uint16_t : 0;
|
||||
@ -275,7 +275,7 @@ typedef struct ATTR_PACKED
|
||||
} tusb_desc_endpoint_t;
|
||||
|
||||
/// USB Other Speed Configuration Descriptor (section 9.6.1 table 9-11)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
|
||||
@ -289,7 +289,7 @@ typedef struct ATTR_PACKED
|
||||
} tusb_desc_other_speed_t;
|
||||
|
||||
/// USB Device Qualifier Descriptor (section 9.6.1 table 9-9)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Device Qualifier Type
|
||||
@ -304,7 +304,7 @@ typedef struct ATTR_PACKED
|
||||
} tusb_desc_device_qualifier_t;
|
||||
|
||||
/// USB Interface Association Descriptor (IAD ECN)
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
|
||||
@ -320,13 +320,13 @@ typedef struct ATTR_PACKED
|
||||
} tusb_desc_interface_assoc_t;
|
||||
|
||||
/// USB Header Descriptor
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type
|
||||
} tusb_desc_header_t;
|
||||
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type
|
||||
@ -337,9 +337,9 @@ typedef struct ATTR_PACKED
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Types
|
||||
*------------------------------------------------------------------*/
|
||||
typedef struct ATTR_PACKED{
|
||||
typedef struct TU_ATTR_PACKED{
|
||||
union {
|
||||
struct ATTR_PACKED {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
|
||||
uint8_t type : 2; ///< Request type tusb_request_type_t.
|
||||
uint8_t direction : 1; ///< Direction type. tusb_dir_t
|
||||
|
@ -52,7 +52,7 @@ typedef enum
|
||||
USBD_EVENT_FUNC_CALL
|
||||
} dcd_eventid_t;
|
||||
|
||||
typedef struct ATTR_ALIGNED(4)
|
||||
typedef struct TU_ATTR_ALIGNED(4)
|
||||
{
|
||||
uint8_t rhport;
|
||||
uint8_t event_id;
|
||||
@ -100,25 +100,28 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num);
|
||||
// Wake up host
|
||||
void dcd_remote_wakeup(uint8_t rhport);
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Endpoint API
|
||||
* - open : Configure endpoint's registers
|
||||
* - xfer : Submit a transfer. When complete dcd_event_xfer_complete
|
||||
* must be called to notify the stack
|
||||
* - busy : Check if endpoint transferring is complete (TODO remove)
|
||||
* - stall : stall endpoint
|
||||
* - clear_stall : clear stall, data toggle is also reset to DATA0
|
||||
*------------------------------------------------------------------*/
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Configure endpoint's registers according to descriptor
|
||||
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
|
||||
|
||||
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
|
||||
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
|
||||
|
||||
// Check if endpoint transferring is complete (TODO remove)
|
||||
bool dcd_edpt_busy (uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// Stall endpoint
|
||||
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// clear stall, data toggle is also reset to DATA0
|
||||
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Event Function
|
||||
* Called by DCD to notify USBD
|
||||
* Called by DCD to notify device stack
|
||||
*------------------------------------------------------------------*/
|
||||
void dcd_event_handler(dcd_event_t const * event, bool in_isr);
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "tusb.h"
|
||||
#include "usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
#include "dcd.h"
|
||||
|
||||
#ifndef CFG_TUD_TASK_QUEUE_SZ
|
||||
#define CFG_TUD_TASK_QUEUE_SZ 16
|
||||
@ -40,7 +41,7 @@
|
||||
// Device Data
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
struct ATTR_PACKED
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
volatile uint8_t connected : 1;
|
||||
volatile uint8_t configured : 1;
|
||||
@ -51,8 +52,8 @@ typedef struct {
|
||||
uint8_t self_powered : 1; // configuration descriptor's attribute
|
||||
};
|
||||
|
||||
// uint8_t ep_busy_mask[2]; // bit mask for busy endpoint
|
||||
uint8_t ep_stall_mask[2]; // bit mask for stalled endpoint
|
||||
uint8_t ep_busy_map[2]; // bit mask for busy endpoint
|
||||
uint8_t ep_stall_map[2]; // bit map for stalled endpoint
|
||||
|
||||
uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
|
||||
uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid )
|
||||
@ -287,6 +288,10 @@ void tud_task (void)
|
||||
{
|
||||
// Invoke the class callback associated with the endpoint address
|
||||
uint8_t const ep_addr = event.xfer_complete.ep_addr;
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
_usbd_dev.ep_busy_map[dir] = (uint8_t) tu_bit_clear(_usbd_dev.ep_busy_map[dir], epnum);
|
||||
|
||||
if ( 0 == tu_edpt_number(ep_addr) )
|
||||
{
|
||||
@ -621,7 +626,7 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
|
||||
|
||||
case DCD_EVENT_SUSPEND:
|
||||
// NOTE: When plugging/unplugging device, the D+/D- state are unstable and can accidentally meet the
|
||||
// SUSPEND condition ( Idle for 3ms ). Some MCUs such as samd don't distinguish suspend vs disconnect as well.
|
||||
// SUSPEND condition ( Idle for 3ms ). Some MCUs such as SAMD doesn't distinguish suspend vs disconnect as well.
|
||||
// We will skip handling SUSPEND/RESUME event if not currently connected
|
||||
if ( _usbd_dev.connected )
|
||||
{
|
||||
@ -643,7 +648,8 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
|
||||
break;
|
||||
|
||||
case DCD_EVENT_XFER_COMPLETE:
|
||||
// skip zero-length control status complete event, should dcd notifies us.
|
||||
// skip zero-length control status complete event, should DCD notify us.
|
||||
// TODO could cause issue with actual zero length data used by class such as DFU
|
||||
if ( (0 == tu_edpt_number(event->xfer_complete.ep_addr)) && (event->xfer_complete.len == 0) ) break;
|
||||
|
||||
osal_queue_send(_usbd_q, event, in_isr);
|
||||
@ -733,13 +739,37 @@ void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr)
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
|
||||
{
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
TU_VERIFY( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) );
|
||||
|
||||
_usbd_dev.ep_busy_map[dir] = (uint8_t) tu_bit_set(_usbd_dev.ep_busy_map[dir], epnum);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
return tu_bit_test(_usbd_dev.ep_busy_map[dir], epnum);
|
||||
}
|
||||
|
||||
|
||||
void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
dcd_edpt_stall(rhport, ep_addr);
|
||||
_usbd_dev.ep_stall_mask[dir] = (uint8_t) tu_bit_set(_usbd_dev.ep_stall_mask[dir], epnum);
|
||||
_usbd_dev.ep_stall_map[dir] = (uint8_t) tu_bit_set(_usbd_dev.ep_stall_map[dir], epnum);
|
||||
}
|
||||
|
||||
void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
@ -748,7 +778,7 @@ void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
dcd_edpt_clear_stall(rhport, ep_addr);
|
||||
_usbd_dev.ep_stall_mask[dir] = (uint8_t) tu_bit_clear(_usbd_dev.ep_stall_mask[dir], epnum);
|
||||
_usbd_dev.ep_stall_map[dir] = (uint8_t) tu_bit_clear(_usbd_dev.ep_stall_map[dir], epnum);
|
||||
}
|
||||
|
||||
bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr)
|
||||
@ -758,7 +788,7 @@ bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr)
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
return tu_bit_test(_usbd_dev.ep_stall_mask[dir], epnum);
|
||||
return tu_bit_test(_usbd_dev.ep_stall_map[dir], epnum);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -35,7 +35,7 @@
|
||||
#endif
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "device/dcd.h"
|
||||
#include "dcd.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
@ -76,17 +76,17 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index);
|
||||
uint16_t const* tud_descriptor_string_cb(uint8_t index);
|
||||
|
||||
// Invoked when device is mounted (configured)
|
||||
ATTR_WEAK void tud_mount_cb(void);
|
||||
TU_ATTR_WEAK void tud_mount_cb(void);
|
||||
|
||||
// Invoked when device is unmounted
|
||||
ATTR_WEAK void tud_umount_cb(void);
|
||||
TU_ATTR_WEAK void tud_umount_cb(void);
|
||||
|
||||
// Invoked when usb bus is suspended
|
||||
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
|
||||
ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en);
|
||||
TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en);
|
||||
|
||||
// Invoked when usb bus is resumed
|
||||
ATTR_WEAK void tud_resume_cb(void);
|
||||
TU_ATTR_WEAK void tud_resume_cb(void);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interface Descriptor Template
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "tusb.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
#include "dcd.h"
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -33,11 +33,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL API for stack management
|
||||
//--------------------------------------------------------------------+
|
||||
bool usbd_init (void);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Carry out Data and Status stage of control transfer
|
||||
// - If len = 0, it is equivalent to sending status only
|
||||
// - If len > wLength : it will be truncated
|
||||
@ -46,6 +47,12 @@ bool usbd_control_xfer(uint8_t rhport, tusb_control_request_t const * request, v
|
||||
// Send STATUS (zero length) packet
|
||||
bool usbd_control_status(uint8_t rhport, tusb_control_request_t const * request);
|
||||
|
||||
// Submit a usb transfer
|
||||
bool usbd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
|
||||
|
||||
// Check if endpoint transferring is complete
|
||||
bool usbd_edpt_busy (uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr);
|
||||
void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr);
|
||||
bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr);
|
||||
|
@ -44,7 +44,7 @@
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
// Periodic frame list must be 4K alignment
|
||||
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
|
||||
|
||||
// EHCI portable
|
||||
uint32_t hcd_ehci_register_addr(uint8_t rhport);
|
||||
|
@ -95,7 +95,7 @@ typedef union {
|
||||
}ehci_link_t;
|
||||
|
||||
/// Queue Element Transfer Descriptor
|
||||
/// Qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with ATTR_ALIGNED(32)
|
||||
/// Qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with TU_ATTR_ALIGNED(32)
|
||||
typedef struct
|
||||
{
|
||||
// Word 0: Next QTD Pointer
|
||||
@ -137,7 +137,7 @@ typedef struct
|
||||
TU_VERIFY_STATIC( sizeof(ehci_qtd_t) == 32, "size is not correct" );
|
||||
|
||||
/// Queue Head
|
||||
typedef struct ATTR_ALIGNED(32)
|
||||
typedef struct TU_ATTR_ALIGNED(32)
|
||||
{
|
||||
// Word 0: Next QHD
|
||||
ehci_link_t next;
|
||||
@ -185,7 +185,7 @@ typedef struct ATTR_ALIGNED(32)
|
||||
TU_VERIFY_STATIC( sizeof(ehci_qhd_t) == 64, "size is not correct" );
|
||||
|
||||
/// Highspeed Isochronous Transfer Descriptor (section 3.3)
|
||||
typedef struct ATTR_ALIGNED(32) {
|
||||
typedef struct TU_ATTR_ALIGNED(32) {
|
||||
// Word 0: Next Link Pointer
|
||||
ehci_link_t next;
|
||||
|
||||
@ -217,7 +217,7 @@ typedef struct ATTR_ALIGNED(32) {
|
||||
TU_VERIFY_STATIC( sizeof(ehci_itd_t) == 64, "size is not correct" );
|
||||
|
||||
/// Split (Full-Speed) Isochronous Transfer Descriptor
|
||||
typedef struct ATTR_ALIGNED(32)
|
||||
typedef struct TU_ATTR_ALIGNED(32)
|
||||
{
|
||||
// Word 0: Next Link Pointer
|
||||
ehci_link_t next;
|
||||
@ -442,7 +442,7 @@ typedef struct
|
||||
}control[CFG_TUSB_HOST_DEVICE_MAX+1];
|
||||
|
||||
ehci_qhd_t qhd_pool[HCD_MAX_ENDPOINT];
|
||||
ehci_qtd_t qtd_pool[HCD_MAX_XFER] ATTR_ALIGNED(32);
|
||||
ehci_qtd_t qtd_pool[HCD_MAX_XFER] TU_ATTR_ALIGNED(32);
|
||||
|
||||
ehci_registers_t* regs;
|
||||
}ehci_data_t;
|
||||
|
@ -45,7 +45,7 @@ typedef struct
|
||||
}usbh_hub_t;
|
||||
|
||||
CFG_TUSB_MEM_SECTION static usbh_hub_t hub_data[CFG_TUSB_HOST_DEVICE_MAX];
|
||||
ATTR_ALIGNED(4) CFG_TUSB_MEM_SECTION static uint8_t hub_enum_buffer[sizeof(descriptor_hub_desc_t)];
|
||||
TU_ATTR_ALIGNED(4) CFG_TUSB_MEM_SECTION static uint8_t hub_enum_buffer[sizeof(descriptor_hub_desc_t)];
|
||||
|
||||
//OSAL_SEM_DEF(hub_enum_semaphore);
|
||||
//static osal_semaphore_handle_t hub_enum_sem_hdl;
|
||||
|
@ -81,7 +81,7 @@
|
||||
//indicators. See Section 11.5.3.
|
||||
//D15...D8: Reserved
|
||||
|
||||
typedef struct ATTR_PACKED{
|
||||
typedef struct TU_ATTR_PACKED{
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
|
||||
uint8_t bNbrPorts;
|
||||
@ -135,7 +135,7 @@ enum{
|
||||
// data in response of HUB_REQUEST_GET_STATUS, wIndex = 0 (hub)
|
||||
typedef struct {
|
||||
union{
|
||||
struct ATTR_PACKED {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint16_t local_power_source : 1;
|
||||
uint16_t over_current : 1;
|
||||
uint16_t : 14;
|
||||
@ -150,7 +150,7 @@ TU_VERIFY_STATIC( sizeof(hub_status_response_t) == 4, "size is not correct");
|
||||
// data in response of HUB_REQUEST_GET_STATUS, wIndex = Port num
|
||||
typedef struct {
|
||||
union {
|
||||
struct ATTR_PACKED {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint16_t connect_status : 1;
|
||||
uint16_t port_enable : 1;
|
||||
uint16_t suspend : 1;
|
||||
|
@ -124,7 +124,7 @@ enum {
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(256) static ohci_data_t ohci_data;
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(256) static ohci_data_t ohci_data;
|
||||
|
||||
static ohci_ed_t * const p_ed_head[] =
|
||||
{
|
||||
|
@ -65,7 +65,7 @@ typedef struct {
|
||||
volatile uint16_t frame_pad;
|
||||
volatile uint32_t done_head;
|
||||
uint8_t reserved[116+4]; // TODO try to make use of this area if possible, extra 4 byte to make the whole struct size = 256
|
||||
}ohci_hcca_t; // ATTR_ALIGNED(256)
|
||||
}ohci_hcca_t; // TU_ATTR_ALIGNED(256)
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" );
|
||||
|
||||
@ -76,7 +76,7 @@ typedef struct {
|
||||
}ohci_td_item_t;
|
||||
|
||||
|
||||
typedef struct ATTR_ALIGNED(16)
|
||||
typedef struct TU_ATTR_ALIGNED(16)
|
||||
{
|
||||
// Word 0
|
||||
uint32_t used : 1;
|
||||
@ -102,7 +102,7 @@ typedef struct ATTR_ALIGNED(16)
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == 16, "size is not correct" );
|
||||
|
||||
typedef struct ATTR_ALIGNED(16)
|
||||
typedef struct TU_ATTR_ALIGNED(16)
|
||||
{
|
||||
// Word 0
|
||||
uint32_t dev_addr : 7;
|
||||
@ -137,7 +137,7 @@ typedef struct ATTR_ALIGNED(16)
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(ohci_ed_t) == 16, "size is not correct" );
|
||||
|
||||
typedef struct ATTR_ALIGNED(32)
|
||||
typedef struct TU_ATTR_ALIGNED(32)
|
||||
{
|
||||
/*---------- Word 1 ----------*/
|
||||
uint32_t starting_frame : 16;
|
||||
@ -163,7 +163,7 @@ typedef struct ATTR_ALIGNED(32)
|
||||
TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" );
|
||||
|
||||
// structure with member alignment required from large to small
|
||||
typedef struct ATTR_ALIGNED(256)
|
||||
typedef struct TU_ATTR_ALIGNED(256)
|
||||
{
|
||||
ohci_hcca_t hcca;
|
||||
|
||||
|
@ -109,7 +109,7 @@ CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[CFG_TUSB_HOST_DEVICE_MAX+1];
|
||||
OSAL_QUEUE_DEF(OPT_MODE_HOST, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t);
|
||||
static osal_queue_t _usbh_q;
|
||||
|
||||
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(4) static uint8_t _usbh_ctrl_buf[CFG_TUSB_HOST_ENUM_BUFFER_SIZE];
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _usbh_ctrl_buf[CFG_TUSB_HOST_ENUM_BUFFER_SIZE];
|
||||
|
||||
//------------- Reporter Task Data -------------//
|
||||
|
||||
|
@ -77,13 +77,13 @@ static inline bool tuh_device_is_configured(uint8_t dev_addr)
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION CALLBACK
|
||||
//--------------------------------------------------------------------+
|
||||
ATTR_WEAK uint8_t tuh_device_attached_cb (tusb_desc_device_t const *p_desc_device);
|
||||
TU_ATTR_WEAK uint8_t tuh_device_attached_cb (tusb_desc_device_t const *p_desc_device);
|
||||
|
||||
/** Callback invoked when device is mounted (configured) */
|
||||
ATTR_WEAK void tuh_mount_cb (uint8_t dev_addr);
|
||||
TU_ATTR_WEAK void tuh_mount_cb (uint8_t dev_addr);
|
||||
|
||||
/** Callback invoked when device is unmounted (bus reset/unplugged) */
|
||||
ATTR_WEAK void tuh_umount_cb(uint8_t dev_addr);
|
||||
TU_ATTR_WEAK void tuh_umount_cb(uint8_t dev_addr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CLASS-USBH & INTERNAL API
|
||||
|
@ -34,8 +34,8 @@
|
||||
/*------------------------------------------------------------------*/
|
||||
/* MACRO TYPEDEF CONSTANT ENUM
|
||||
*------------------------------------------------------------------*/
|
||||
static ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
|
||||
static ATTR_ALIGNED(4) uint8_t _setup_packet[8];
|
||||
static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
|
||||
static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
|
||||
|
||||
// Setup the control endpoint 0.
|
||||
static void bus_reset(void)
|
||||
|
@ -35,7 +35,7 @@
|
||||
/* MACRO TYPEDEF CONSTANT ENUM
|
||||
*------------------------------------------------------------------*/
|
||||
static UsbDeviceDescBank sram_registers[8][2];
|
||||
static ATTR_ALIGNED(4) uint8_t _setup_packet[8];
|
||||
static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
|
||||
|
||||
// Setup the control endpoint 0.
|
||||
static void bus_reset(void)
|
||||
|
@ -67,7 +67,7 @@ enum {
|
||||
CMDSTAT_VBUS_DEBOUNCED_MASK = TU_BIT(28),
|
||||
};
|
||||
|
||||
typedef struct ATTR_PACKED
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
// Bits 21:6 (aligned 64) used in conjunction with bit 31:22 of DATABUFSTART
|
||||
volatile uint16_t buffer_offset;
|
||||
@ -101,7 +101,7 @@ typedef struct
|
||||
|
||||
xfer_dma_t dma[EP_COUNT];
|
||||
|
||||
ATTR_ALIGNED(64) uint8_t setup_packet[8];
|
||||
TU_ATTR_ALIGNED(64) uint8_t setup_packet[8];
|
||||
}dcd_data_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -109,7 +109,7 @@ typedef struct
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// EP list must be 256-byte aligned
|
||||
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(256) static dcd_data_t _dcd;
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(256) static dcd_data_t _dcd;
|
||||
|
||||
static inline uint16_t get_buf_offset(void const * buffer)
|
||||
{
|
||||
|
@ -37,7 +37,7 @@
|
||||
//--------------------------------------------------------------------+
|
||||
#define DCD_ENDPOINT_MAX 32
|
||||
|
||||
typedef struct ATTR_ALIGNED(4)
|
||||
typedef struct TU_ATTR_ALIGNED(4)
|
||||
{
|
||||
//------------- Word 0 -------------//
|
||||
uint32_t next;
|
||||
@ -91,7 +91,7 @@ typedef struct
|
||||
|
||||
} dcd_data_t;
|
||||
|
||||
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(128) static dcd_data_t _dcd;
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(128) static dcd_data_t _dcd;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -46,16 +46,16 @@
|
||||
|
||||
typedef struct {
|
||||
// Must be at 2K alignment
|
||||
dcd_qhd_t qhd[QHD_MAX] ATTR_ALIGNED(64);
|
||||
dcd_qtd_t qtd[QHD_MAX] ATTR_ALIGNED(32);
|
||||
dcd_qhd_t qhd[QHD_MAX] TU_ATTR_ALIGNED(64);
|
||||
dcd_qtd_t qtd[QHD_MAX] TU_ATTR_ALIGNED(32);
|
||||
}dcd_data_t;
|
||||
|
||||
#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
|
||||
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(2048) static dcd_data_t dcd_data0;
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048) static dcd_data_t dcd_data0;
|
||||
#endif
|
||||
|
||||
#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
|
||||
CFG_TUSB_MEM_SECTION ATTR_ALIGNED(2048) static dcd_data_t dcd_data1;
|
||||
CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048) static dcd_data_t dcd_data1;
|
||||
#endif
|
||||
|
||||
static LPC_USBHS_T * const LPC_USB[2] = { LPC_USB0, LPC_USB1 };
|
||||
|
@ -39,7 +39,7 @@
|
||||
#define IN_EP_BASE (USB_OTG_INEndpointTypeDef *) (USB_OTG_FS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE)
|
||||
#define FIFO_BASE(_x) (uint32_t *) (USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE + (_x) * USB_OTG_FIFO_SIZE)
|
||||
|
||||
static ATTR_ALIGNED(4) uint32_t _setup_packet[6];
|
||||
static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6];
|
||||
static uint8_t _setup_offs; // We store up to 3 setup packets.
|
||||
|
||||
typedef struct {
|
||||
|
@ -131,7 +131,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4)
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_OS
|
||||
|
3
test/ceedling
Normal file
3
test/ceedling
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
ruby vendor/ceedling/bin/ceedling $*
|
@ -9,61 +9,42 @@
|
||||
:use_exceptions: TRUE
|
||||
:use_test_preprocessor: TRUE
|
||||
:use_auxiliary_dependencies: TRUE
|
||||
:use_deep_dependencies: TRUE
|
||||
:build_root: build
|
||||
:build_root: _build
|
||||
# :release_build: TRUE
|
||||
:test_file_prefix: test_
|
||||
:which_ceedling: vendor/ceedling
|
||||
:default_tasks:
|
||||
- test:all
|
||||
|
||||
:release_build:
|
||||
:output: test_tinyusb_lpc175x_6x.exe
|
||||
:use_assembly: FALSE
|
||||
#:release_build:
|
||||
# :output: MyApp.out
|
||||
# :use_assembly: FALSE
|
||||
|
||||
:environment:
|
||||
|
||||
:extension:
|
||||
:executable: .exe
|
||||
:executable: .out
|
||||
|
||||
:paths:
|
||||
:test:
|
||||
- +:test/**
|
||||
- -:test/support
|
||||
:source:
|
||||
- ../../tinyusb/**
|
||||
- +:../../mcu/lpc175x_6x/**
|
||||
#- +:../../demos/device/keyboard/*
|
||||
- -:../../demos/
|
||||
# - ../vendor/freertos/freertos/Source/*
|
||||
# - ../vendor/freertos/freertos/Source/portable/MSVC-MingW/*
|
||||
|
||||
- ../src/**
|
||||
:support:
|
||||
- ../support
|
||||
- test/support
|
||||
|
||||
:defines:
|
||||
# in order to add common defines:
|
||||
# 1) remove the trailing [] from the :common: section
|
||||
# 2) add entries to the :common: section (e.g. :test: has TEST defined)
|
||||
:commmon: &common_defines
|
||||
- CFG_TUSB_MCU=MCU_LPC175X_6X -std=gnu99
|
||||
# - -mx32
|
||||
- CORE_M4
|
||||
- __CODE_RED
|
||||
- _TINY_USB_SOURCE_FILE_
|
||||
- _TEST_
|
||||
:commmon: &common_defines []
|
||||
:test:
|
||||
- *common_defines
|
||||
- TEST
|
||||
:test_preprocess:
|
||||
- *common_defines
|
||||
# :release:
|
||||
# :release_preprocess:
|
||||
|
||||
#:flags:
|
||||
# :test:
|
||||
# :compile:
|
||||
# :dcd_lpc175x_6x.c:
|
||||
# - -DMCU=MCU_LPC175X_6X
|
||||
|
||||
# Ceedling defaults to using gcc for compiling, linking, etc.
|
||||
# As [:tools] is blank, gcc will be used (so long as it's in your system path)
|
||||
# See documentation to configure a given toolchain for use
|
||||
- TEST
|
||||
|
||||
:cmock:
|
||||
:mock_prefix: mock_
|
||||
@ -72,8 +53,6 @@
|
||||
:plugins:
|
||||
- :ignore
|
||||
- :callback
|
||||
- :array
|
||||
#:ignore: :args_only
|
||||
:treat_as:
|
||||
uint8: HEX8
|
||||
uint16: HEX16
|
||||
@ -81,11 +60,32 @@
|
||||
int8: INT8
|
||||
bool: UINT8
|
||||
|
||||
:gcov:
|
||||
:html_report_type: basic
|
||||
|
||||
#:tools:
|
||||
# Ceedling defaults to using gcc for compiling, linking, etc.
|
||||
# As [:tools] is blank, gcc will be used (so long as it's in your system path)
|
||||
# See documentation to configure a given toolchain for use
|
||||
|
||||
# LIBRARIES
|
||||
# These libraries are automatically injected into the build process. Those specified as
|
||||
# common will be used in all types of builds. Otherwise, libraries can be injected in just
|
||||
# tests or releases. These options are MERGED with the options in supplemental yaml files.
|
||||
:libraries:
|
||||
:placement: :end
|
||||
:flag: "${1}" # or "-L ${1}" for example
|
||||
:common: &common_libraries []
|
||||
:test:
|
||||
- *common_libraries
|
||||
:release:
|
||||
- *common_libraries
|
||||
|
||||
:plugins:
|
||||
:load_paths:
|
||||
- ../vendor/ceedling/plugins
|
||||
- vendor/ceedling/plugins
|
||||
:enabled:
|
||||
#- stdout_pretty_tests_report
|
||||
- stdout_ide_tests_report
|
||||
- stdout_pretty_tests_report
|
||||
- module_generator
|
||||
- raw_output_report
|
||||
...
|
@ -1,88 +1,103 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CONTROLLER CONFIGURATION
|
||||
//--------------------------------------------------------------------+
|
||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | OPT_MODE_DEVICE)
|
||||
#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_NONE)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HOST CONFIGURATION
|
||||
//--------------------------------------------------------------------+
|
||||
#define CFG_TUSB_HOST_DEVICE_MAX 5 // TODO be a part of HUB config
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUH_HUB 1
|
||||
#define CFG_TUH_HID_KEYBOARD 1
|
||||
#define CFG_TUH_HID_MOUSE 1
|
||||
#define CFG_TUH_MSC 1
|
||||
#define CFG_TUSB_HOST_HID_GENERIC 0
|
||||
#define CFG_TUH_CDC 1
|
||||
#define CFG_TUH_CDC_RNDIS 0
|
||||
|
||||
// Test support
|
||||
#define TEST_CONTROLLER_HOST_START_INDEX \
|
||||
( ((CONTROLLER_HOST_NUMBER == 1) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST)) ? 1 : 0)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------+
|
||||
#define CFG_TUD_ENDOINT0_SIZE 64
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC 1
|
||||
#define CFG_TUD_MSC 1
|
||||
#define CFG_TUD_HID_KEYBOARD 1
|
||||
#define CFG_TUD_HID_MOUSE 1
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define CFG_TUSB_DEBUG 1
|
||||
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define RANDOM(n) (rand()%(n))
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
||||
|
||||
/** @} */
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by compiler flags for flexibility
|
||||
#ifndef CFG_TUSB_MCU
|
||||
//#error CFG_TUSB_MCU must be defined
|
||||
#define CFG_TUSB_MCU OPT_MCU_NRF5X
|
||||
#endif
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX
|
||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
|
||||
#else
|
||||
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
|
||||
#endif
|
||||
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
|
||||
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
|
||||
// #define CFG_TUSB_DEBUG 0
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||
* into those specific section.
|
||||
* e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#define CFG_TUD_ENDOINT0_SIZE 64
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC 1
|
||||
#define CFG_TUD_MSC 1
|
||||
#define CFG_TUD_HID 1
|
||||
|
||||
#define CFG_TUD_MIDI 1
|
||||
#define CFG_TUD_CUSTOM_CLASS 0
|
||||
|
||||
//------------- CDC -------------//
|
||||
|
||||
// FIFO size of CDC TX and RX
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE 64
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE 64
|
||||
|
||||
//------------- MSC -------------//
|
||||
|
||||
// Buffer size of Device Mass storage
|
||||
#define CFG_TUD_MSC_BUFSIZE 512
|
||||
|
||||
//------------- HID -------------//
|
||||
|
||||
// Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_BUFSIZE 16
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
@ -25,7 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "fifo.h"
|
||||
#include "tusb_fifo.h"
|
||||
|
||||
#define FIFO_SIZE 10
|
||||
TU_FIFO_DEF(ff, FIFO_SIZE, uint8_t, false);
|
||||
@ -39,16 +39,14 @@ void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Tests
|
||||
//--------------------------------------------------------------------+
|
||||
void test_normal(void)
|
||||
{
|
||||
uint8_t i;
|
||||
for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(&ff, &i);
|
||||
|
||||
for(i=0; i < FIFO_SIZE; i++)
|
||||
{
|
||||
tu_fifo_write(&ff, &i);
|
||||
}
|
||||
|
||||
for(i=0; i < FIFO_SIZE; i++)
|
||||
for(uint8_t i=0; i < FIFO_SIZE; i++)
|
||||
{
|
||||
uint8_t c;
|
||||
tu_fifo_read(&ff, &c);
|
||||
@ -56,24 +54,36 @@ void test_normal(void)
|
||||
}
|
||||
}
|
||||
|
||||
void test_is_empty(void)
|
||||
void test_peek(void)
|
||||
{
|
||||
uint8_t temp;
|
||||
TEST_ASSERT_TRUE(tu_fifo_is_empty(&ff));
|
||||
tu_fifo_write(&ff, &temp);
|
||||
TEST_ASSERT_FALSE(tu_fifo_is_empty(&ff));
|
||||
|
||||
temp = 10; tu_fifo_write(&ff, &temp);
|
||||
temp = 20; tu_fifo_write(&ff, &temp);
|
||||
temp = 30; tu_fifo_write(&ff, &temp);
|
||||
|
||||
temp = 0;
|
||||
|
||||
tu_fifo_peek(&ff, &temp);
|
||||
TEST_ASSERT_EQUAL(10, temp);
|
||||
|
||||
tu_fifo_peek_at(&ff, 1, &temp);
|
||||
TEST_ASSERT_EQUAL(20, temp);
|
||||
}
|
||||
|
||||
void test_is_full(void)
|
||||
void test_empty(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
TEST_ASSERT_FALSE(tu_fifo_is_full(&ff));
|
||||
|
||||
for(i=0; i < FIFO_SIZE; i++)
|
||||
{
|
||||
tu_fifo_write(&ff, &i);
|
||||
}
|
||||
|
||||
TEST_ASSERT_TRUE(tu_fifo_is_full(&ff));
|
||||
uint8_t temp;
|
||||
TEST_ASSERT_TRUE(tu_fifo_empty(&ff));
|
||||
tu_fifo_write(&ff, &temp);
|
||||
TEST_ASSERT_FALSE(tu_fifo_empty(&ff));
|
||||
}
|
||||
|
||||
void test_full(void)
|
||||
{
|
||||
TEST_ASSERT_FALSE(tu_fifo_full(&ff));
|
||||
|
||||
for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(&ff, &i);
|
||||
|
||||
TEST_ASSERT_TRUE(tu_fifo_full(&ff));
|
||||
}
|
316
test/vendor/ceedling/bin/ceedling
vendored
Normal file
316
test/vendor/ceedling/bin/ceedling
vendored
Normal file
@ -0,0 +1,316 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
#these are always used
|
||||
require 'rubygems'
|
||||
require 'fileutils'
|
||||
|
||||
# Check for the main project file (either the one defined in the ENV or the default)
|
||||
main_filepath = ENV['CEEDLING_MAIN_PROJECT_FILE']
|
||||
project_found = (!main_filepath.nil? && File.exists?(main_filepath))
|
||||
if (!project_found)
|
||||
main_filepath = "project.yml"
|
||||
project_found = File.exists?(main_filepath)
|
||||
end
|
||||
|
||||
def is_windows?
|
||||
return ((RbConfig::CONFIG['host_os'] =~ /mswin|mingw/) ? true : false) if defined?(RbConfig)
|
||||
return ((Config::CONFIG['host_os'] =~ /mswin|mingw/) ? true : false)
|
||||
end
|
||||
|
||||
unless (project_found)
|
||||
#===================================== We Do Not Have A Project ================================================
|
||||
|
||||
puts "Welcome to Ceedling!"
|
||||
require 'thor'
|
||||
|
||||
def here
|
||||
File.dirname(__FILE__) + "/.."
|
||||
end
|
||||
|
||||
class CeedlingTasks < Thor
|
||||
include Thor::Actions
|
||||
|
||||
desc "new PROJECT_NAME", "create a new ceedling project"
|
||||
method_option :no_docs, :type => :boolean, :default => false, :desc => "No docs in vendor directory"
|
||||
method_option :nodocs, :type => :boolean, :default => false
|
||||
method_option :as_gem, :type => :boolean, :default => false, :desc => "Create the scaffold using Ceedling as a gem instead of filling in the vendor directory. Implies --no-docs."
|
||||
method_option :asgem, :type => :boolean, :default => false
|
||||
method_option :with_ignore, :type => :boolean, :default => false, :desc => "Create a gitignore file for ignoring ceedling generated files."
|
||||
method_option :withignore, :type => :boolean, :default => false
|
||||
method_option :no_configs, :type => :boolean, :default => false, :desc => "Don't install starter configuration files."
|
||||
method_option :noconfigs, :type => :boolean, :default => false
|
||||
def new(name, silent = false)
|
||||
copy_assets_and_create_structure(name, silent, false, options)
|
||||
end
|
||||
|
||||
desc "upgrade PROJECT_NAME", "upgrade ceedling for a project (not req'd if gem used)"
|
||||
method_option :no_docs, :type => :boolean, :default => false, :desc => "No docs in vendor directory"
|
||||
method_option :nodocs, :type => :boolean, :default => false
|
||||
method_option :no_configs, :type => :boolean, :default => true, :desc => "Don't install starter configuration files."
|
||||
method_option :noconfigs, :type => :boolean, :default => false
|
||||
def upgrade(name, silent = false)
|
||||
copy_assets_and_create_structure(name, silent, true, options)
|
||||
end
|
||||
|
||||
no_commands do
|
||||
def copy_assets_and_create_structure(name, silent=false, force=false, options = {})
|
||||
|
||||
no_docs = options[:no_docs] || options[:nodocs] || false
|
||||
no_configs = options[:no_configs] || options[:noconfigs] || false
|
||||
as_gem = options[:as_gem] || options[:asgem] || false
|
||||
with_ignore = options[:with_ignore] || options[:withignore] || false
|
||||
|
||||
ceedling_path = File.join(name, 'vendor', 'ceedling')
|
||||
source_path = File.join(name, 'src')
|
||||
test_path = File.join(name, 'test')
|
||||
test_support_path = File.join(name, 'test/support')
|
||||
|
||||
[source_path, test_path, test_support_path].each do |d|
|
||||
FileUtils.mkdir_p d
|
||||
end
|
||||
|
||||
unless as_gem
|
||||
FileUtils.mkdir_p ceedling_path
|
||||
|
||||
unless no_docs
|
||||
doc_path = File.join(ceedling_path, 'docs')
|
||||
FileUtils.mkdir_p doc_path
|
||||
|
||||
in_doc_path = lambda {|f| File.join(doc_path, f)}
|
||||
|
||||
doc_files = [
|
||||
'docs/CeedlingPacket.md',
|
||||
'vendor/c_exception/docs/CException.md',
|
||||
'vendor/cmock/docs/CMock_Summary.md',
|
||||
'vendor/unity/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf',
|
||||
'vendor/unity/docs/UnityAssertionsReference.md',
|
||||
'vendor/unity/docs/UnityConfigurationGuide.md',
|
||||
'vendor/unity/docs/UnityGettingStartedGuide.md',
|
||||
'vendor/unity/docs/UnityHelperScriptsGuide.md',
|
||||
'vendor/unity/docs/ThrowTheSwitchCodingStandard.md',
|
||||
]
|
||||
|
||||
doc_files.each do |f|
|
||||
copy_file(f, in_doc_path.call(File.basename(f)), :force => force)
|
||||
end
|
||||
end
|
||||
|
||||
folders = if as_gem
|
||||
%w{plugins lib}
|
||||
else
|
||||
%w{plugins lib bin}
|
||||
end
|
||||
|
||||
#copy full folders from ceedling gem into project
|
||||
folders.map do |f|
|
||||
{:src => f, :dst => File.join(ceedling_path, f)}
|
||||
end.each do |f|
|
||||
directory(f[:src], f[:dst], :force => force)
|
||||
end
|
||||
|
||||
#copy necessary subcomponents from ceedling gem into project
|
||||
sub_components = [
|
||||
{:src => 'vendor/c_exception/lib/', :dst => 'vendor/c_exception/lib'},
|
||||
{:src => 'vendor/c_exception/release/', :dst => 'vendor/c_exception/release'},
|
||||
{:src => 'vendor/cmock/config/', :dst => 'vendor/cmock/config'},
|
||||
{:src => 'vendor/cmock/lib/', :dst => 'vendor/cmock/lib'},
|
||||
{:src => 'vendor/cmock/release/', :dst => 'vendor/cmock/release'},
|
||||
{:src => 'vendor/cmock/src/', :dst => 'vendor/cmock/src'},
|
||||
{:src => 'vendor/deep_merge/lib/', :dst => 'vendor/deep_merge/lib'},
|
||||
{:src => 'vendor/diy/lib', :dst => 'vendor/diy/lib'},
|
||||
{:src => 'vendor/unity/auto/', :dst => 'vendor/unity/auto'},
|
||||
{:src => 'vendor/unity/release/', :dst => 'vendor/unity/release'},
|
||||
{:src => 'vendor/unity/src/', :dst => 'vendor/unity/src'},
|
||||
]
|
||||
|
||||
sub_components.each do |c|
|
||||
directory(c[:src], File.join(ceedling_path, c[:dst]), :force => force)
|
||||
end
|
||||
end
|
||||
|
||||
unless (no_configs)
|
||||
if as_gem
|
||||
copy_file(File.join('assets', 'project_as_gem.yml'), File.join(name, 'project.yml'), :force => force)
|
||||
else
|
||||
copy_file(File.join('assets', 'project_with_guts.yml'), File.join(name, 'project.yml'), :force => force)
|
||||
if is_windows?
|
||||
copy_file(File.join('assets', 'ceedling.cmd'), File.join(name, 'ceedling.cmd'), :force => force)
|
||||
else
|
||||
copy_file(File.join('assets', 'ceedling'), File.join(name, 'ceedling'), :force => force)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (with_ignore)
|
||||
copy_file(File.join('assets', 'default_gitignore'), File.join(name, '.gitignore'), :force => force)
|
||||
end
|
||||
|
||||
unless silent
|
||||
puts "\n"
|
||||
puts "Project '#{name}' #{force ? "upgraded" : "created"}!"
|
||||
puts " - Tool documentation is located in vendor/ceedling/docs" if (not no_docs) and (not as_gem)
|
||||
puts " - Execute 'ceedling help' to view available test & build tasks"
|
||||
puts ''
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc "examples", "list available example projects"
|
||||
def examples()
|
||||
puts "Available sample projects:"
|
||||
FileUtils.cd(File.join(here, "examples")) do
|
||||
Dir["*"].each {|proj| puts " #{proj}"}
|
||||
end
|
||||
end
|
||||
|
||||
desc "example PROJ_NAME [DEST]", "new specified example project (in DEST, if specified)"
|
||||
def example(proj_name, dest=nil)
|
||||
if dest.nil? then dest = proj_name end
|
||||
|
||||
invoke :new, [dest, true]
|
||||
|
||||
dest_src = File.join(dest,'src')
|
||||
dest_test = File.join(dest,'test')
|
||||
dest_project = File.join(dest,'project.yml')
|
||||
|
||||
directory "examples/#{proj_name}/src", dest_src
|
||||
directory "examples/#{proj_name}/test", dest_test
|
||||
remove_file dest_project
|
||||
copy_file "examples/#{proj_name}/project.yml", dest_project
|
||||
|
||||
puts "\n"
|
||||
puts "Example project '#{proj_name}' created!"
|
||||
puts " - Tool documentation is located in vendor/ceedling/docs"
|
||||
puts " - Execute 'ceedling help' to view available test & build tasks"
|
||||
puts ''
|
||||
end
|
||||
|
||||
desc "version", "return the version of the tools installed"
|
||||
def version()
|
||||
require 'ceedling/version.rb'
|
||||
puts " Ceedling:: #{Ceedling::Version::CEEDLING}"
|
||||
puts " CMock:: #{Ceedling::Version::CMOCK}"
|
||||
puts " Unity:: #{Ceedling::Version::UNITY}"
|
||||
puts " CException:: #{Ceedling::Version::CEXCEPTION}"
|
||||
end
|
||||
end
|
||||
|
||||
if (ARGV[0] =~ /^\-T$/)
|
||||
puts "\n(No Project Detected, Therefore Showing Options to Create Projects)"
|
||||
CeedlingTasks.tasks.each_pair do |k,v|
|
||||
puts v.usage.ljust(25,' ') + v.description
|
||||
end
|
||||
puts "\n"
|
||||
else
|
||||
CeedlingTasks.source_root here
|
||||
CeedlingTasks.start
|
||||
end
|
||||
|
||||
#===================================== We Have A Project Already ================================================
|
||||
else
|
||||
require 'yaml'
|
||||
require 'rbconfig'
|
||||
|
||||
#determine platform
|
||||
platform = begin
|
||||
case(RbConfig::CONFIG['host_os'])
|
||||
when /mswin|mingw|cygwin/i
|
||||
:mswin
|
||||
when /darwin/
|
||||
:osx
|
||||
else
|
||||
:linux
|
||||
end
|
||||
rescue
|
||||
:linux
|
||||
end
|
||||
|
||||
#create our default meta-runner option set
|
||||
options = {
|
||||
:pretest => nil,
|
||||
:args => [],
|
||||
:add_path => [],
|
||||
:path_connector => (platform == :mswin) ? ";" : ":",
|
||||
:graceful_fail => false,
|
||||
:which_ceedling => (Dir.exists?("vendor/ceedling") ? "vendor/ceedling" : 'gem'),
|
||||
:default_tasks => [ 'test:all' ],
|
||||
:list_tasks => false
|
||||
}
|
||||
|
||||
#guess that we need a special script file first if it exists
|
||||
if (platform == :mswin)
|
||||
options[:pretest] = File.exists?("#{ platform.to_s }_setup.bat") ? "#{ platform.to_s }_setup.bat" : nil
|
||||
else
|
||||
options[:pretest] = File.exists?("#{ platform.to_s }_setup.sh") ? "source #{ platform.to_s }_setup.sh" : nil
|
||||
end
|
||||
|
||||
#merge in project settings if they can be found here
|
||||
yaml_options = YAML.load_file(main_filepath)
|
||||
if (yaml_options[:paths])
|
||||
options[:add_path] = yaml_options[:paths][:tools] || []
|
||||
else
|
||||
options[:add_path] = []
|
||||
end
|
||||
options[:graceful_fail] = yaml_options[:graceful_fail] if yaml_options[:graceful_fail]
|
||||
options[:which_ceedling] = yaml_options[:project][:which_ceedling] if (yaml_options[:project] && yaml_options[:project][:which_ceedling])
|
||||
options[:default_tasks] = yaml_options[:default_tasks] if yaml_options[:default_tasks]
|
||||
|
||||
#sort through command line options
|
||||
ARGV.each do |v|
|
||||
case(v)
|
||||
when /^(?:new|examples?|templates?)$/
|
||||
puts "\nOops. You called ceedling with argument '#{v}'.\n" +
|
||||
" This is an operation that will create a new project... \n" +
|
||||
" but it looks like you're already in a project. If you really \n" +
|
||||
" want to do this, try moving to an empty folder.\n\n"
|
||||
abort
|
||||
when /^help$/
|
||||
options[:list_tasks] = true
|
||||
when /^project:(\w+)/
|
||||
ENV['CEEDLING_USER_PROJECT_FILE'] = "#{$1}.yml"
|
||||
else
|
||||
options[:args].push(v)
|
||||
end
|
||||
end
|
||||
|
||||
#add to the path
|
||||
if (options[:add_path] && !options[:add_path].empty?)
|
||||
path = ENV["PATH"]
|
||||
options[:add_path].each do |p|
|
||||
f = File.expand_path(File.dirname(__FILE__),p)
|
||||
path = (f + options[:path_connector] + path) unless path.include? f
|
||||
end
|
||||
ENV["PATH"] = path
|
||||
end
|
||||
|
||||
# Load Ceedling (either through the rakefile OR directly)
|
||||
if (File.exists?("rakefile.rb"))
|
||||
load 'rakefile.rb'
|
||||
else
|
||||
if (options[:which_ceedling] == 'gem')
|
||||
require 'ceedling'
|
||||
else
|
||||
load "#{options[:which_ceedling]}/lib/ceedling.rb"
|
||||
end
|
||||
Ceedling.load_project
|
||||
end
|
||||
|
||||
Rake.application.standard_exception_handling do
|
||||
if options[:list_tasks]
|
||||
# Display helpful task list when requested. This required us to dig into Rake internals a bit
|
||||
Rake.application.define_singleton_method(:name=) {|n| @name = n}
|
||||
Rake.application.name = 'ceedling'
|
||||
Rake.application.options.show_tasks = :tasks
|
||||
Rake.application.options.show_task_pattern = /^(?!.*build).*$/
|
||||
Rake.application.display_tasks_and_comments()
|
||||
else
|
||||
task :default => options[:default_tasks]
|
||||
|
||||
# Run our Tasks!
|
||||
Rake.application.collect_command_line_tasks(options[:args])
|
||||
Rake.application.top_level
|
||||
end
|
||||
end
|
||||
true
|
||||
#===================================================================================================================
|
||||
end
|
292
test/vendor/ceedling/docs/CException.md
vendored
Normal file
292
test/vendor/ceedling/docs/CException.md
vendored
Normal file
@ -0,0 +1,292 @@
|
||||
|
||||
CException
|
||||
==========
|
||||
|
||||
CException is a basic exception framework for C, suitable for use in
|
||||
embedded applications. It provides an exception framework similar in
|
||||
use to C++, but with much less overhead.
|
||||
|
||||
|
||||
CException uses C standard library functions `setjmp` and `longjmp` to
|
||||
operate. As long as the target system has these two functions defined,
|
||||
this library should be useable with very little configuration. It
|
||||
even supports environments where multiple program flows are in use,
|
||||
such as real-time operating systems.
|
||||
|
||||
|
||||
There are about a gabillion exception frameworks using a similar
|
||||
setjmp/longjmp method out there... and there will probably be more
|
||||
in the future. Unfortunately, when we started our last embedded
|
||||
project, all those that existed either (a) did not support multiple
|
||||
tasks (therefore multiple stacks) or (b) were way more complex than
|
||||
we really wanted. CException was born.
|
||||
|
||||
|
||||
*Why use CException?*
|
||||
|
||||
|
||||
0. It's ANSI C, and it beats passing error codes around.
|
||||
1. You want something simple... CException throws a single id. You can
|
||||
define those ID's to be whatever you like. You might even choose which
|
||||
type that number is for your project. But that's as far as it goes.
|
||||
We weren't interested in passing objects or structs or strings...
|
||||
just simple error codes.
|
||||
2. Performance... CException can be configured for single tasking or
|
||||
multitasking. In single tasking, there is very little overhead past
|
||||
the setjmp/longjmp calls (which are already fast). In multitasking,
|
||||
your only additional overhead is the time it takes you to determine
|
||||
a unique task id 0 - num_tasks.
|
||||
|
||||
|
||||
For the latest version, go to [ThrowTheSwitch.org](http://throwtheswitch.org)
|
||||
|
||||
|
||||
CONTENTS OF THIS DOCUMENT
|
||||
=========================
|
||||
|
||||
* Usage
|
||||
* Limitations
|
||||
*API
|
||||
* Configuration
|
||||
* Testing
|
||||
* License
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Code that is to be protected are wrapped in `Try { } Catch { }` blocks.
|
||||
The code directly following the Try call is "protected", meaning that
|
||||
if any Throws occur, program control is directly transferred to the
|
||||
start of the Catch block.
|
||||
|
||||
|
||||
A numerical exception ID is included with Throw, and is made accessible
|
||||
from the Catch block.
|
||||
|
||||
|
||||
Throws can occur from within function calls (nested as deeply as you
|
||||
like) or directly from within the function itself.
|
||||
|
||||
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
|
||||
This library was made to be as fast as possible, and provide basic
|
||||
exception handling. It is not a full-blown exception library. Because
|
||||
of this, there are a few limitations that should be observed in order
|
||||
to successfully utilize this library:
|
||||
|
||||
1. Do not directly "return" from within a `Try` block, nor `goto`
|
||||
into or out of a `Try` block.
|
||||
|
||||
*Why?*
|
||||
|
||||
The `Try` macro allocates some local memory and alters a global
|
||||
pointer. These are cleaned up at the top of the `Catch` macro.
|
||||
Gotos and returns would bypass some of these steps, resulting in
|
||||
memory leaks or unpredictable behavior.
|
||||
|
||||
|
||||
2. If (a) you change local (stack) variables within your `Try` block,
|
||||
AND (b) wish to make use of the updated values after an exception
|
||||
is thrown, those variables should be made `volatile`. Note that this
|
||||
is ONLY for locals and ONLY when you need access to them after a
|
||||
`Throw`.
|
||||
|
||||
*Why?*
|
||||
|
||||
Compilers optimize. There is no way to guarantee that the actual
|
||||
memory location was updated and not just a register unless the
|
||||
variable is marked volatile.
|
||||
|
||||
|
||||
3. Memory which is `malloc`'d or `new`'d is not automatically released
|
||||
when an error is thrown. This will sometimes be desirable, and
|
||||
othertimes may not. It will be the responsibility of the `Catch`
|
||||
block to perform this kind of cleanup.
|
||||
|
||||
*Why?*
|
||||
|
||||
There's just no easy way to track `malloc`'d memory, etc., without
|
||||
replacing or wrapping malloc calls or something like that. This
|
||||
is a light framework, so these options were not desirable.
|
||||
|
||||
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
###Try
|
||||
|
||||
`Try` is a macro which starts a protected block. It MUST be followed by
|
||||
a pair of braces or a single protected line (similar to an 'if'),
|
||||
enclosing the data that is to be protected. It **must** be followed by a
|
||||
`Catch` block (don't worry, you'll get compiler errors to let you know if
|
||||
you mess any of that up).
|
||||
|
||||
|
||||
###Catch(e)
|
||||
|
||||
`Catch` is a macro which ends the `Try` block and starts the error handling
|
||||
block. The `Catch` block is called if and only if an exception was thrown
|
||||
while within the `Try` block. This error was thrown by a `Throw` call
|
||||
somewhere within `Try` (or within a function called within `Try`, or a function
|
||||
called by a function called within `Try`, etc).
|
||||
|
||||
The single parameter `e` is filled with the error code which was thrown.
|
||||
This can be used for reporting, conditional cleanup, etc. (or you can just
|
||||
ignore it if you really want... people ignore return codes all the time,
|
||||
right?). `e` should be of type `EXCEPTION_T`
|
||||
|
||||
|
||||
###Throw(e)
|
||||
|
||||
This is the method of throwing an error. A `Throw` should only occur from within a
|
||||
protected (`Try` ... `Catch`) block, though it may easily be nested many function
|
||||
calls deep without an impact on performance or functionality. `Throw` takes
|
||||
a single argument, which is an exception id which will be passed to `Catch`
|
||||
as the reason for the error.
|
||||
|
||||
If you wish to rethrow an error, this can be done by calling `Throw(e)` with
|
||||
the error code you just caught. It **is** valid to throw from a catch block.
|
||||
|
||||
|
||||
###ExitTry()
|
||||
|
||||
On rare occasion, you might want to immediately exit your current `Try` block
|
||||
but **not** treat this as an error. Don't run the `Catch`. Just start executing
|
||||
from after the `Catch` as if nothing had happened... That's what `ExitTry` is
|
||||
for.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
CException is a mostly portable library. It has one universal
|
||||
dependency, and some macros which are required if working in a
|
||||
multi-tasking environment.
|
||||
|
||||
1. The standard C library setjmp must be available. Since this is part
|
||||
of the standard library, chances are good that you'll be fine.
|
||||
|
||||
2. If working in a multitasking environment, methods for obtaining an
|
||||
index into an array of frames and to get the overall number of
|
||||
id's are required. If the OS supports a method to retrieve Task
|
||||
ID's, and those Tasks are number 0, 1, 2... you are in an ideal
|
||||
situation. Otherwise, a more creative mapping function may be
|
||||
required. Note that this function is likely to be called twice
|
||||
for each protected block and once during a throw. This is the
|
||||
only overhead in the system.
|
||||
|
||||
|
||||
Exception.h
|
||||
-----------
|
||||
|
||||
By convention, most projects include `Exception.h` which defines any
|
||||
further requirements, then calls `CException.h` to do the gruntwork. All
|
||||
of these are optional. You could directly include `CException.h` if
|
||||
you wanted and just use the defaults provided.
|
||||
|
||||
* `EXCEPTION_T`
|
||||
* Set this to the type you want your exception id's to be. Defaults to 'unsigned int'.
|
||||
|
||||
* `EXCEPTION_NONE`
|
||||
* Set this to a number which will never be an exception id in your system. Defaults to `0x5a5a5a5a`.
|
||||
|
||||
* `EXCEPTION_GET_ID`
|
||||
* If in a multi-tasking environment, this should be
|
||||
set to be a call to the function described in #2 above.
|
||||
Defaults to just return `0` all the time (good for
|
||||
single tasking environments)
|
||||
|
||||
* `EXCEPTION_NUM_ID`
|
||||
* If in a multi-tasking environment, this should be set
|
||||
to the number of ID's required (usually the number of
|
||||
tasks in the system). Defaults to `1` (for single
|
||||
tasking environments).
|
||||
|
||||
* `CEXCEPTION_NO_CATCH_HANDLER(id)`
|
||||
* This macro can be optionally specified.
|
||||
It allows you to specify code to be called when a Throw
|
||||
is made outside of `Try` ... `Catch` protection. Consider
|
||||
this the emergency fallback plan for when something has
|
||||
gone terribly wrong.
|
||||
|
||||
|
||||
You may also want to include any header files which will commonly be
|
||||
needed by the rest of your application where it uses exception handling
|
||||
here. For example, OS header files or exception codes would be useful.
|
||||
|
||||
|
||||
Finally, there are some hook macros which you can implement to inject
|
||||
your own target-specific code in particular places. It is a rare instance
|
||||
where you will need these, but they are here if you need them:
|
||||
|
||||
|
||||
* `CEXCEPTION_HOOK_START_TRY`
|
||||
* called immediately before the Try block
|
||||
|
||||
* `CEXCEPTION_HOOK_HAPPY_TRY`
|
||||
* called immediately after the Try block if no exception was thrown
|
||||
|
||||
* `CEXCEPTION_HOOK_AFTER_TRY`
|
||||
* called immediately after the Try block OR before an exception is caught
|
||||
|
||||
* `CEXCEPTION_HOOK_START_CATCH`
|
||||
* called immediately before the catch
|
||||
|
||||
|
||||
|
||||
TESTING
|
||||
-------
|
||||
|
||||
|
||||
If you want to validate that CException works with your tools or that
|
||||
it works with your custom configuration, you may want to run the test
|
||||
suite.
|
||||
|
||||
|
||||
The test suite included makes use of the `Unity` Test Framework. It will
|
||||
require a native C compiler. The example makefile uses MinGW's gcc.
|
||||
Modify the makefile to include the proper paths to tools, then run `make`
|
||||
to compile and run the test application.
|
||||
|
||||
* `C_COMPILER`
|
||||
* The C compiler to use to perform the tests
|
||||
|
||||
* `C_LIBS`
|
||||
* The path to the C libraries (including setjmp)
|
||||
|
||||
* `UNITY_DIR`
|
||||
* The path to the Unity framework (required to run tests)
|
||||
(get it at [ThrowTheSwitch.org](http://throwtheswitch.org))
|
||||
|
||||
|
||||
|
||||
LICENSE
|
||||
-------
|
||||
|
||||
This software is licensed under the MIT License
|
||||
|
||||
Copyright (c) 2007-2017 Mark VanderVoord
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
603
test/vendor/ceedling/docs/CMock_Summary.md
vendored
Normal file
603
test/vendor/ceedling/docs/CMock_Summary.md
vendored
Normal file
@ -0,0 +1,603 @@
|
||||
CMock: A Summary
|
||||
================
|
||||
|
||||
*[ThrowTheSwitch.org](http://throwtheswitch.org)*
|
||||
|
||||
*This documentation is released under a Creative Commons 3.0 Attribution Share-Alike License*
|
||||
|
||||
|
||||
What Exactly Are We Talking About Here?
|
||||
---------------------------------------
|
||||
|
||||
CMock is a nice little tool which takes your header files and creates
|
||||
a Mock interface for it so that you can more easily unit test modules
|
||||
that touch other modules. For each function prototype in your
|
||||
header, like this one:
|
||||
|
||||
int DoesSomething(int a, int b);
|
||||
|
||||
|
||||
...you get an automatically generated DoesSomething function
|
||||
that you can link to instead of your real DoesSomething function.
|
||||
By using this Mocked version, you can then verify that it receives
|
||||
the data you want, and make it return whatever data you desire,
|
||||
make it throw errors when you want, and more... Create these for
|
||||
everything your latest real module touches, and you're suddenly
|
||||
in a position of power: You can control and verify every detail
|
||||
of your latest creation.
|
||||
|
||||
To make that easier, CMock also gives you a bunch of functions
|
||||
like the ones below, so you can tell that generated DoesSomething
|
||||
function how to behave for each test:
|
||||
|
||||
void DoesSomething_ExpectAndReturn(int a, int b, int toReturn);
|
||||
void DoesSomething_ExpectAndThrow(int a, int b, EXCEPTION_T error);
|
||||
void DoesSomething_StubWithCallback(CMOCK_DoesSomething_CALLBACK YourCallback);
|
||||
void DoesSomething_IgnoreAndReturn(int toReturn);
|
||||
|
||||
|
||||
You can pile a bunch of these back to back, and it remembers what
|
||||
you wanted to pass when, like so:
|
||||
|
||||
test_CallsDoesSomething_ShouldDoJustThat(void)
|
||||
{
|
||||
DoesSomething_ExpectAndReturn(1,2,3);
|
||||
DoesSomething_ExpectAndReturn(4,5,6);
|
||||
DoesSomething_ExpectAndThrow(7,8, STATUS_ERROR_OOPS);
|
||||
|
||||
CallsDoesSomething( );
|
||||
}
|
||||
|
||||
|
||||
This test will call CallsDoesSomething, which is the function
|
||||
we are testing. We are expecting that function to call DoesSomething
|
||||
three times. The first time, we check to make sure it's called
|
||||
as DoesSomething(1, 2) and we'll magically return a 3. The second
|
||||
time we check for DoesSomething(4, 5) and we'll return a 6. The
|
||||
third time we verify DoesSomething(7, 8) and we'll throw an error
|
||||
instead of returning anything. If CallsDoesSomething gets
|
||||
any of this wrong, it fails the test. It will fail if you didn't
|
||||
call DoesSomething enough, or too much, or with the wrong arguments,
|
||||
or in the wrong order.
|
||||
|
||||
CMock is based on Unity, which it uses for all internal testing.
|
||||
It uses Ruby to do all the main work (versions 2.0.0 and above).
|
||||
|
||||
|
||||
Installing
|
||||
==========
|
||||
|
||||
The first thing you need to do to install CMock is to get yourself
|
||||
a copy of Ruby. If you're on linux or osx, you probably already
|
||||
have it. You can prove it by typing the following:
|
||||
|
||||
ruby --version
|
||||
|
||||
|
||||
If it replied in a way that implies ignorance, then you're going to
|
||||
need to install it. You can go to [ruby-lang](https://ruby-lang.org)
|
||||
to get the latest version. You're also going to need to do that if it
|
||||
replied with a version that is older than 2.0.0. Go ahead. We'll wait.
|
||||
|
||||
Once you have Ruby, you have three options:
|
||||
|
||||
* Clone the latest [CMock repo on github](https://github.com/ThrowTheSwitch/CMock/)
|
||||
* Download the latest [CMock zip from github](https://github.com/ThrowTheSwitch/CMock/)
|
||||
* Install Ceedling (which has it built in!) through your commandline using `gem install ceedling`.
|
||||
|
||||
|
||||
Generated Mock Module Summary
|
||||
=============================
|
||||
|
||||
In addition to the mocks themselves, CMock will generate the
|
||||
following functions for use in your tests. The expect functions
|
||||
are always generated. The other functions are only generated
|
||||
if those plugins are enabled:
|
||||
|
||||
|
||||
Expect:
|
||||
-------
|
||||
|
||||
Your basic staple Expects which will be used for most of your day
|
||||
to day CMock work. By calling this, you are telling CMock that you
|
||||
expect that function to be called during your test. It also specifies
|
||||
which arguments you expect it to be called with, and what return
|
||||
value you want returned when that happens. You can call this function
|
||||
multiple times back to back in order to queue up multiple calls.
|
||||
|
||||
* `void func(void)` => `void func_Expect(void)`
|
||||
* `void func(params)` => `void func_Expect(expected_params)`
|
||||
* `retval func(void)` => `void func_ExpectAndReturn(retval_to_return)`
|
||||
* `retval func(params)` => `void func_ExpectAndReturn(expected_params, retval_to_return)`
|
||||
|
||||
|
||||
ExpectAnyArgs:
|
||||
--------------
|
||||
|
||||
This behaves just like the Expects calls, except that it doesn't really
|
||||
care what the arguments are that the mock gets called with. It still counts
|
||||
the number of times the mock is called and it still handles return values
|
||||
if there are some.
|
||||
|
||||
* `void func(void)` => `void func_ExpectAnyArgs(void)`
|
||||
* `void func(params)` => `void func_ExpectAnyArgs(void)`
|
||||
* `retval func(void)` => `void func_ExpectAnyArgsAndReturn(retval_to_return)`
|
||||
* `retval func(params)` => `void func_ExpectAnyArgsAndReturn(retval_to_return)`
|
||||
|
||||
|
||||
Array:
|
||||
------
|
||||
|
||||
An ExpectWithArray is another variant of Expect. Like expect, it cares about
|
||||
the number of times a mock is called, the arguments it is called with, and the
|
||||
values it is to return. This variant has another feature, though. For anything
|
||||
that resembles a pointer or array, it breaks the argument into TWO arguments.
|
||||
The first is the original pointer. The second specify the number of elements
|
||||
it is to verify of that array. If you specify 1, it'll check one object. If 2,
|
||||
it'll assume your pointer is pointing at the first of two elements in an array.
|
||||
If you specify zero elements, it will check just the pointer if
|
||||
`:smart` mode is configured or fail if `:compare_data` is set.
|
||||
|
||||
* `void func(void)` => (nothing. In fact, an additional function is only generated if the params list contains pointers)
|
||||
* `void func(ptr * param, other)` => `void func_ExpectWithArray(ptr* param, int param_depth, other)`
|
||||
* `retval func(void)` => (nothing. In fact, an additional function is only generated if the params list contains pointers)
|
||||
* `retval func(other, ptr* param)` => `void func_ExpectWithArrayAndReturn(other, ptr* param, int param_depth, retval_to_return)`
|
||||
|
||||
|
||||
Ignore:
|
||||
-------
|
||||
|
||||
Maybe you don't care about the number of times a particular function is called or
|
||||
the actual arguments it is called with. In that case, you want to use Ignore. Ignore
|
||||
only needs to be called once per test. It will then ignore any further calls to that
|
||||
particular mock. The IgnoreAndReturn works similarly, except that it has the added
|
||||
benefit of knowing what to return when that call happens. If the mock is called more
|
||||
times than IgnoreAndReturn was called, it will keep returning the last value without
|
||||
complaint. If it's called less times, it will also ignore that. You SAID you didn't
|
||||
care how many times it was called, right?
|
||||
|
||||
* `void func(void)` => `void func_Ignore(void)`
|
||||
* `void func(params)` => `void func_Ignore(void)`
|
||||
* `retval func(void)` => `void func_IgnoreAndReturn(retval_to_return)`
|
||||
* `retval func(params)` => `void func_IgnoreAndReturn(retval_to_return)`
|
||||
|
||||
|
||||
Ignore Arg:
|
||||
------------
|
||||
|
||||
Maybe you overall want to use Expect and its similar variations, but you don't care
|
||||
what is passed to a particular argument. This is particularly useful when that argument
|
||||
is a pointer to a value that is supposed to be filled in by the function. You don't want
|
||||
to use ExpectAnyArgs, because you still care about the other arguments. Instead, before
|
||||
any of your Expect calls are made, you can call this function. It tells CMock to ignore
|
||||
a particular argument for the rest of this test, for this mock function.
|
||||
|
||||
* `void func(params)` => `void func_IgnoreArg_paramName(void)`
|
||||
|
||||
|
||||
ReturnThruPtr:
|
||||
--------------
|
||||
|
||||
Another option which operates on a particular argument of a function is the ReturnThruPtr
|
||||
plugin. For every argument that resembles a pointer or reference, CMock generates an
|
||||
instance of this function. Just as the AndReturn functions support injecting one or more
|
||||
return values into a queue, this function lets you specify one or more return values which
|
||||
are queued up and copied into the space being pointed at each time the mock is called.
|
||||
|
||||
* `void func(param1)` => `void func_ReturnThruPtr_paramName(val_to_return)`
|
||||
* => `void func_ReturnArrayThruPtr_paramName(cal_to_return, len)`
|
||||
* => `void func_ReturnMemThruPtr_paramName(val_to_return, size)`
|
||||
|
||||
|
||||
Callback:
|
||||
---------
|
||||
|
||||
If all those other options don't work, and you really need to do something custom, you
|
||||
still have a choice. As soon as you stub a callback in a test, it will call the callback
|
||||
whenever the mock is encountered and return the retval returned from the callback (if any)
|
||||
instead of performing the usual expect checks. It can be configured to check the arguments
|
||||
first (like expects) or just jump directly to the callback.
|
||||
|
||||
* `void func(void)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)`
|
||||
where `CMOCK_func_CALLBACK` looks like: `void func(int NumCalls)`
|
||||
* `void func(params)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)`
|
||||
where `CMOCK_func_CALLBACK` looks like: `void func(params, int NumCalls)`
|
||||
* `retval func(void)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)`
|
||||
where `CMOCK_func_CALLBACK` looks like: `retval func(int NumCalls)`
|
||||
* `retval func(params)` => `void func_StubWithCallback(CMOCK_func_CALLBACK callback)`
|
||||
where `CMOCK_func_CALLBACK` looks like: `retval func(params, int NumCalls)`
|
||||
|
||||
|
||||
Cexception:
|
||||
-----------
|
||||
|
||||
Finally, if you are using Cexception for error handling, you can use this to throw errors
|
||||
from inside mocks. Like Expects, it remembers which call was supposed to throw the error,
|
||||
and it still checks parameters first.
|
||||
|
||||
* `void func(void)` => `void func_ExpectAndThrow(value_to_throw)`
|
||||
* `void func(params)` => `void func_ExpectAndThrow(expected_params, value_to_throw)`
|
||||
* `retval func(void)` => `void func_ExpectAndThrow(value_to_throw)`
|
||||
* `retval func(params)` => `void func_ExpectAndThrow(expected_params, value_to_throw)`
|
||||
|
||||
|
||||
|
||||
Running CMock
|
||||
=============
|
||||
|
||||
CMock is a Ruby script and class. You can therefore use it directly
|
||||
from the command line, or include it in your own scripts or rakefiles.
|
||||
|
||||
|
||||
Mocking from the Command Line
|
||||
-----------------------------
|
||||
|
||||
After unpacking CMock, you will find cmock.rb in the 'lib' directory.
|
||||
This is the file that you want to run. It takes a list of header files
|
||||
to be mocked, as well as an optional yaml file for a more detailed
|
||||
configuration (see config options below).
|
||||
|
||||
For example, this will create three mocks using the configuration
|
||||
specified in MyConfig.yml:
|
||||
|
||||
ruby cmock.rb -oMyConfig.yml super.h duper.h awesome.h
|
||||
|
||||
And this will create two mocks using the default configuration:
|
||||
|
||||
ruby cmock.rb ../mocking/stuff/is/fun.h ../try/it/yourself.h
|
||||
|
||||
|
||||
Mocking From Scripts or Rake
|
||||
----------------------------
|
||||
|
||||
CMock can be used directly from your own scripts or from a rakefile.
|
||||
Start by including cmock.rb, then create an instance of CMock.
|
||||
When you create your instance, you may initialize it in one of
|
||||
three ways.
|
||||
|
||||
You may specify nothing, allowing it to run with default settings:
|
||||
|
||||
require 'cmock.rb'
|
||||
cmock = CMock.new
|
||||
|
||||
You may specify a YAML file containing the configuration options
|
||||
you desire:
|
||||
|
||||
cmock = CMock.new('../MyConfig.yml')
|
||||
|
||||
You may specify the options explicitly:
|
||||
|
||||
cmock = Cmock.new(:plugins => [:cexception, :ignore], :mock_path => 'my/mocks/')
|
||||
|
||||
|
||||
Config Options:
|
||||
---------------
|
||||
|
||||
The following configuration options can be specified in the
|
||||
yaml file or directly when instantiating.
|
||||
|
||||
Passed as Ruby, they look like this:
|
||||
|
||||
{ :attributes => [“__funky”, “__intrinsic”], :when_ptr => :compare }
|
||||
|
||||
Defined in the yaml file, they look more like this:
|
||||
|
||||
:cmock:
|
||||
:attributes:
|
||||
- __funky
|
||||
- __intrinsic
|
||||
:when_ptr: :compare
|
||||
|
||||
In all cases, you can just include the things that you want to override
|
||||
from the defaults. We've tried to specify what the defaults are below.
|
||||
|
||||
* `:attributes`:
|
||||
These are attributes that CMock should ignore for you for testing
|
||||
purposes. Custom compiler extensions and externs are handy things to
|
||||
put here. If your compiler is choking on some extended syntax, this
|
||||
is often a good place to look.
|
||||
|
||||
* defaults: ['__ramfunc', '__irq', '__fiq', 'register', 'extern']
|
||||
* **note:** this option will reinsert these attributes onto the mock's calls.
|
||||
If that isn't what you are looking for, check out :strippables.
|
||||
|
||||
* `:c_calling_conventions`:
|
||||
Similarly, CMock may need to understand which C calling conventions
|
||||
might show up in your codebase. If it encounters something it doesn't
|
||||
recognize, it's not going to mock it. We have the most common covered,
|
||||
but there are many compilers out there, and therefore many other options.
|
||||
|
||||
* defaults: ['__stdcall', '__cdecl', '__fastcall']
|
||||
* **note:** this option will reinsert these attributes onto the mock's calls.
|
||||
If that isn't what you are looking for, check out :strippables.
|
||||
|
||||
* `:callback_after_arg_check`:
|
||||
Tell `:callback` plugin to do the normal argument checking **before** it
|
||||
calls the callback function by setting this to true. When false, the
|
||||
callback function is called **instead** of the argument verification.
|
||||
|
||||
* default: false
|
||||
|
||||
* `:callback_include_count`:
|
||||
Tell `:callback` plugin to include an extra parameter to specify the
|
||||
number of times the callback has been called. If set to false, the
|
||||
callback has the same interface as the mocked function. This can be
|
||||
handy when you're wanting to use callback as a stub.
|
||||
|
||||
* default: true
|
||||
|
||||
* `:cexception_include`:
|
||||
Tell `:cexception` plugin where to find CException.h... You only need to
|
||||
define this if it's not in your build path already... which it usually
|
||||
will be for the purpose of your builds.
|
||||
|
||||
* default: *nil*
|
||||
|
||||
* `:enforce_strict_ordering`:
|
||||
CMock always enforces the order that you call a particular function,
|
||||
so if you expect GrabNabber(int size) to be called three times, it
|
||||
will verify that the sizes are in the order you specified. You might
|
||||
*also* want to make sure that all different functions are called in a
|
||||
particular order. If so, set this to true.
|
||||
|
||||
* default: false
|
||||
|
||||
* `:framework`:
|
||||
Currently the only option is `:unity.` Eventually if we support other
|
||||
unity test frameworks (or if you write one for us), they'll get added
|
||||
here.
|
||||
|
||||
: default: :unity
|
||||
|
||||
* `:includes`:
|
||||
An array of additional include files which should be added to the
|
||||
mocks. Useful for global types and definitions used in your project.
|
||||
There are more specific versions if you care WHERE in the mock files
|
||||
the includes get placed. You can define any or all of these options.
|
||||
|
||||
* `:includes`
|
||||
* `:includes_h_pre_orig_header`
|
||||
* `:includes_h_post_orig_header`
|
||||
* `:includes_c_pre_header`
|
||||
* `:includes_c_post_header`
|
||||
* default: nil #for all 5 options
|
||||
|
||||
* `:memcmp_if_unknown`:
|
||||
C developers create a lot of types, either through typedef or preprocessor
|
||||
macros. CMock isn't going to automatically know what you were thinking all
|
||||
the time (though it tries its best). If it comes across a type it doesn't
|
||||
recognize, you have a choice on how you want it to handle it. It can either
|
||||
perform a raw memory comparison and report any differences, or it can fail
|
||||
with a meaningful message. Either way, this feature will only happen after
|
||||
all other mechanisms have failed (The thing encountered isn't a standard
|
||||
type. It isn't in the :treat_as list. It isn't in a custom unity_helper).
|
||||
|
||||
* default: true
|
||||
|
||||
* `:mock_path`:
|
||||
The directory where you would like the mock files generated to be
|
||||
placed.
|
||||
|
||||
* default: mocks
|
||||
|
||||
* `:mock_prefix`:
|
||||
The prefix to prepend to your mock files. For example, if it's “Mock”, a file
|
||||
“USART.h” will get a mock called “MockUSART.c”. This CAN be used with a suffix
|
||||
at the same time.
|
||||
|
||||
* default: Mock
|
||||
|
||||
* `:mock_suffix`:
|
||||
The suffix to append to your mock files. For example, it it's "_Mock", a file
|
||||
"USART.h" will get a mock called "USART_Mock.h". This CAN be used with a prefix
|
||||
at the same time.
|
||||
|
||||
* default: ""
|
||||
|
||||
* `:plugins`:
|
||||
An array of which plugins to enable. ':expect' is always active. Also
|
||||
available currently:
|
||||
|
||||
* `:ignore`
|
||||
* `:ignore_arg`
|
||||
* `:expect_any_args`
|
||||
* `:array`
|
||||
* `:cexception`
|
||||
* `:callback`
|
||||
* `:return_thru_ptr`
|
||||
|
||||
* `:strippables`:
|
||||
An array containing a list of items to remove from the header
|
||||
before deciding what should be mocked. This can be something simple
|
||||
like a compiler extension CMock wouldn't recognize, or could be a
|
||||
regex to reject certain function name patterns. This is a great way to
|
||||
get rid of compiler extensions when your test compiler doesn't support
|
||||
them. For example, use `:strippables: ['(?:functionName\s*\(+.*?\)+)']`
|
||||
to prevent a function `functionName` from being mocked. By default, it
|
||||
is ignoring all gcc attribute extensions.
|
||||
|
||||
* default: ['(?:__attribute__\s*\(+.*?\)+)']
|
||||
|
||||
* `:subdir`:
|
||||
This is a relative subdirectory for your mocks. Set this to e.g. "sys" in
|
||||
order to create a mock for `sys/types.h` in `(:mock_path)/sys/`.
|
||||
|
||||
* default: ""
|
||||
|
||||
* `:treat_as`:
|
||||
The `:treat_as` list is a shortcut for when you have created typedefs
|
||||
of standard types. Why create a custom unity helper for UINT16 when
|
||||
the unity function TEST_ASSERT_EQUAL_HEX16 will work just perfectly?
|
||||
Just add 'UINT16' => 'HEX16' to your list (actually, don't. We already
|
||||
did that one for you). Maybe you have a type that is a pointer to an
|
||||
array of unsigned characters? No problem, just add 'UINT8_T*' =>
|
||||
'HEX8*'
|
||||
|
||||
* NOTE: unlike the other options, your specifications MERGE with the
|
||||
default list. Therefore, if you want to override something, you must
|
||||
reassign it to something else (or to *nil* if you don't want it)
|
||||
|
||||
* default:
|
||||
* 'int': 'INT'
|
||||
* 'char': 'INT8'
|
||||
* 'short': 'INT16'
|
||||
* 'long': 'INT'
|
||||
* 'int8': 'INT8'
|
||||
* 'int16': 'INT16'
|
||||
* 'int32': 'INT'
|
||||
* 'int8_t': 'INT8'
|
||||
* 'int16_t': 'INT16'
|
||||
* 'int32_t': 'INT'
|
||||
* 'INT8_T': 'INT8'
|
||||
* 'INT16_T': 'INT16'
|
||||
* 'INT32_T': 'INT'
|
||||
* 'bool': 'INT'
|
||||
* 'bool_t': 'INT'
|
||||
* 'BOOL': 'INT'
|
||||
* 'BOOL_T': 'INT'
|
||||
* 'unsigned int': 'HEX32'
|
||||
* 'unsigned long': 'HEX32'
|
||||
* 'uint32': 'HEX32'
|
||||
* 'uint32_t': 'HEX32'
|
||||
* 'UINT32': 'HEX32'
|
||||
* 'UINT32_T': 'HEX32'
|
||||
* 'void*': 'HEX8_ARRAY'
|
||||
* 'unsigned short': 'HEX16'
|
||||
* 'uint16': 'HEX16'
|
||||
* 'uint16_t': 'HEX16'
|
||||
* 'UINT16': 'HEX16'
|
||||
* 'UINT16_T': 'HEX16'
|
||||
* 'unsigned char': 'HEX8'
|
||||
* 'uint8': 'HEX8'
|
||||
* 'uint8_t': 'HEX8'
|
||||
* 'UINT8': 'HEX8'
|
||||
* 'UINT8_T': 'HEX8'
|
||||
* 'char*': 'STRING'
|
||||
* 'pCHAR': 'STRING'
|
||||
* 'cstring': 'STRING'
|
||||
* 'CSTRING': 'STRING'
|
||||
* 'float': 'FLOAT'
|
||||
* 'double': 'FLOAT'
|
||||
|
||||
* `:treat_as_void`:
|
||||
We've seen "fun" legacy systems typedef 'void' with a custom type,
|
||||
like MY_VOID. Add any instances of those to this list to help CMock
|
||||
understand how to deal with your code.
|
||||
|
||||
* default: []
|
||||
|
||||
* `:treat_externs`:
|
||||
This specifies how you want CMock to handle functions that have been
|
||||
marked as extern in the header file. Should it mock them?
|
||||
|
||||
* `:include` will mock externed functions
|
||||
* `:exclude` will ignore externed functions (default).
|
||||
|
||||
* `:unity_helper_path`:
|
||||
If you have created a header with your own extensions to unity to
|
||||
handle your own types, you can set this argument to that path. CMock
|
||||
will then automagically pull in your helpers and use them. The only
|
||||
trick is that you make sure you follow the naming convention:
|
||||
`UNITY_TEST_ASSERT_EQUAL_YourType`. If it finds macros of the right
|
||||
shape that match that pattern, it'll use them.
|
||||
|
||||
* default: []
|
||||
|
||||
* `:verbosity`:
|
||||
How loud should CMock be?
|
||||
|
||||
* 0 for errors only
|
||||
* 1 for errors and warnings
|
||||
* 2 for normal (default)
|
||||
* 3 for verbose
|
||||
|
||||
* `:weak`:
|
||||
When set this to some value, the generated mocks are defined as weak
|
||||
symbols using the configured format. This allows them to be overridden
|
||||
in particular tests.
|
||||
|
||||
* Set to '__attribute ((weak))' for weak mocks when using GCC.
|
||||
* Set to any non-empty string for weak mocks when using IAR.
|
||||
* default: ""
|
||||
|
||||
* `:when_no_prototypes`:
|
||||
When you give CMock a header file and ask it to create a mock out of
|
||||
it, it usually contains function prototypes (otherwise what was the
|
||||
point?). You can control what happens when this isn't true. You can
|
||||
set this to `:warn,` `:ignore,` or `:error`
|
||||
|
||||
* default: :warn
|
||||
|
||||
* `:when_ptr`:
|
||||
You can customize how CMock deals with pointers (c strings result in
|
||||
string comparisons... we're talking about **other** pointers here). Your
|
||||
options are `:compare_ptr` to just verify the pointers are the same,
|
||||
`:compare_data` or `:smart` to verify that the data is the same.
|
||||
`:compare_data` and `:smart` behaviors will change slightly based on
|
||||
if you have the array plugin enabled. By default, they compare a
|
||||
single element of what is being pointed to. So if you have a pointer
|
||||
to a struct called ORGAN_T, it will compare one ORGAN_T (whatever that
|
||||
is).
|
||||
|
||||
* default: :smart
|
||||
|
||||
* `:fail_on_unexpected_calls`:
|
||||
By default, CMock will fail a test if a mock is called without _Expect and _Ignore
|
||||
called first. While this forces test writers to be more explicit in their expectations,
|
||||
it can clutter tests with _Expect or _Ignore calls for functions which are not the focus
|
||||
of the test. While this is a good indicator that this module should be refactored, some
|
||||
users are not fans of the additional noise.
|
||||
|
||||
Therefore, :fail_on_unexpected_calls can be set to false to force all mocks to start with
|
||||
the assumption that they are operating as _Ignore unless otherwise specified.
|
||||
|
||||
* default: true
|
||||
* **note:**
|
||||
If this option is disabled, the mocked functions will return
|
||||
a default value (0) when called (and only if they have to return something of course).
|
||||
|
||||
|
||||
Compiled Options:
|
||||
-----------------
|
||||
|
||||
A number of #defines also exist for customizing the cmock experience.
|
||||
Feel free to pass these into your compiler or whatever is most
|
||||
convenient. CMock will otherwise do its best to guess what you want
|
||||
based on other settings, particularly Unity's settings.
|
||||
|
||||
* `CMOCK_MEM_STATIC` or `CMOCK_MEM_DYNAMIC`
|
||||
Define one of these to determine if you want to dynamically add
|
||||
memory during tests as required from the heap. If static, you
|
||||
can control the total footprint of Cmock. If dynamic, you will
|
||||
need to make sure you make some heap space available for Cmock.
|
||||
|
||||
* `CMOCK_MEM_SIZE`
|
||||
In static mode this is the total amount of memory you are allocating
|
||||
to Cmock. In Dynamic mode this is the size of each chunk allocated
|
||||
at once (larger numbers grab more memory but require less mallocs).
|
||||
|
||||
* `CMOCK_MEM_ALIGN`
|
||||
The way to align your data to. Not everything is as flexible as
|
||||
a PC, as most embedded designers know. This defaults to 2, meaning
|
||||
align to the closest 2^2 -> 4 bytes (32 bits). You can turn off alignment
|
||||
by setting 0, force alignment to the closest uint16 with 1 or even
|
||||
to the closest uint64 with 3.
|
||||
|
||||
* `CMOCK_MEM_PTR_AS_INT`
|
||||
This is used internally to hold pointers... it needs to be big
|
||||
enough. On most processors a pointer is the same as an unsigned
|
||||
long... but maybe that's not true for yours?
|
||||
|
||||
* `CMOCK_MEM_INDEX_TYPE`
|
||||
This needs to be something big enough to point anywhere in Cmock's
|
||||
memory space... usually it's an unsigned int.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
You can look in the [examples directory](/examples/) for a couple of examples on how
|
||||
you might tool CMock into your build process. You may also want to consider
|
||||
using [Ceedling](https://throwtheswitch.org/ceedling). Please note that
|
||||
these examples are meant to show how the build process works. They have
|
||||
failing tests ON PURPOSE to show what that would look like. Don't be alarmed. ;)
|
||||
|
2060
test/vendor/ceedling/docs/CeedlingPacket.md
vendored
Normal file
2060
test/vendor/ceedling/docs/CeedlingPacket.md
vendored
Normal file
File diff suppressed because it is too large
Load Diff
206
test/vendor/ceedling/docs/ThrowTheSwitchCodingStandard.md
vendored
Normal file
206
test/vendor/ceedling/docs/ThrowTheSwitchCodingStandard.md
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
# ThrowTheSwitch.org Coding Standard
|
||||
|
||||
Hi. Welcome to the coding standard for ThrowTheSwitch.org. For the most part,
|
||||
we try to follow these standards to unify our contributors' code into a cohesive
|
||||
unit (puns intended). You might find places where these standards aren't
|
||||
followed. We're not perfect. Please be polite where you notice these discrepancies
|
||||
and we'll try to be polite when we notice yours.
|
||||
|
||||
;)
|
||||
|
||||
|
||||
## Why Have A Coding Standard?
|
||||
|
||||
Being consistent makes code easier to understand. We've tried to keep
|
||||
our standard simple because we also believe that we can only expect someone to
|
||||
follow something that is understandable. Please do your best.
|
||||
|
||||
|
||||
## Our Philosophy
|
||||
|
||||
Before we get into details on syntax, let's take a moment to talk about our
|
||||
vision for these tools. We're C developers and embedded software developers.
|
||||
These tools are great to test any C code, but catering to embedded software has
|
||||
made us more tolerant of compiler quirks. There are a LOT of quirky compilers
|
||||
out there. By quirky I mean "doesn't follow standards because they feel like
|
||||
they have a license to do as they wish."
|
||||
|
||||
Our philosophy is "support every compiler we can". Most often, this means that
|
||||
we aim for writing C code that is standards compliant (often C89... that seems
|
||||
to be a sweet spot that is almost always compatible). But it also means these
|
||||
tools are tolerant of things that aren't common. Some that aren't even
|
||||
compliant. There are configuration options to override the size of standard
|
||||
types. There are configuration options to force Unity to not use certain
|
||||
standard library functions. A lot of Unity is configurable and we have worked
|
||||
hard to make it not TOO ugly in the process.
|
||||
|
||||
Similarly, our tools that parse C do their best. They aren't full C parsers
|
||||
(yet) and, even if they were, they would still have to accept non-standard
|
||||
additions like gcc extensions or specifying `@0x1000` to force a variable to
|
||||
compile to a particular location. It's just what we do, because we like
|
||||
everything to Just Work™.
|
||||
|
||||
Speaking of having things Just Work™, that's our second philosophy. By that, we
|
||||
mean that we do our best to have EVERY configuration option have a logical
|
||||
default. We believe that if you're working with a simple compiler and target,
|
||||
you shouldn't need to configure very much... we try to make the tools guess as
|
||||
much as they can, but give the user the power to override it when it's wrong.
|
||||
|
||||
|
||||
## Naming Things
|
||||
|
||||
Let's talk about naming things. Programming is all about naming things. We name
|
||||
files, functions, variables, and so much more. While we're not always going to
|
||||
find the best name for something, we actually put a bit of effort into
|
||||
finding *What Something WANTS to be Called*™.
|
||||
|
||||
When naming things, we follow this hierarchy, the first being the
|
||||
most important to us (but we do all four when possible):
|
||||
1. Readable
|
||||
2. Descriptive
|
||||
3. Consistent
|
||||
4. Memorable
|
||||
|
||||
|
||||
#### Readable
|
||||
|
||||
We want to read our code. This means we like names and flow that are more
|
||||
naturally read. We try to avoid double negatives. We try to avoid cryptic
|
||||
abbreviations (sticking to ones we feel are common).
|
||||
|
||||
|
||||
#### Descriptive
|
||||
|
||||
We like descriptive names for things, especially functions and variables.
|
||||
Finding the right name for something is an important endeavor. You might notice
|
||||
from poking around our code that this often results in names that are a little
|
||||
longer than the average. Guilty. We're okay with a bit more typing if it
|
||||
means our code is easier to understand.
|
||||
|
||||
There are two exceptions to this rule that we also stick to as religiously as
|
||||
possible:
|
||||
|
||||
First, while we realize hungarian notation (and similar systems for encoding
|
||||
type information into variable names) is providing a more descriptive name, we
|
||||
feel that (for the average developer) it takes away from readability and is to be avoided.
|
||||
|
||||
Second, loop counters and other local throw-away variables often have a purpose
|
||||
which is obvious. There's no need, therefore, to get carried away with complex
|
||||
naming. We find i, j, and k are better loop counters than loopCounterVar or
|
||||
whatnot. We only break this rule when we see that more description could improve
|
||||
understanding of an algorithm.
|
||||
|
||||
|
||||
#### Consistent
|
||||
|
||||
We like consistency, but we're not really obsessed with it. We try to name our
|
||||
configuration macros in a consistent fashion... you'll notice a repeated use of
|
||||
UNITY_EXCLUDE_BLAH or UNITY_USES_BLAH macros. This helps users avoid having to
|
||||
remember each macro's details.
|
||||
|
||||
|
||||
#### Memorable
|
||||
|
||||
Where ever it doesn't violate the above principles, we try to apply memorable
|
||||
names. Sometimes this means using something that is simply descriptive, but
|
||||
often we strive for descriptive AND unique... we like quirky names that stand
|
||||
out in our memory and are easier to search for. Take a look through the file
|
||||
names in Ceedling and you'll get a good idea of what we are talking about here.
|
||||
Why use preprocess when you can use preprocessinator? Or what better describes a
|
||||
module in charge of invoking tasks during releases than release_invoker? Don't
|
||||
get carried away. The names are still descriptive and fulfill the above
|
||||
requirements, but they don't feel stale.
|
||||
|
||||
|
||||
## C and C++ Details
|
||||
|
||||
We don't really want to add to the style battles out there. Tabs or spaces?
|
||||
How many spaces? Where do the braces go? These are age-old questions that will
|
||||
never be answered... or at least not answered in a way that will make everyone
|
||||
happy.
|
||||
|
||||
We've decided on our own style preferences. If you'd like to contribute to these
|
||||
projects (and we hope that you do), then we ask if you do your best to follow
|
||||
the same. It will only hurt a little. We promise.
|
||||
|
||||
|
||||
#### Whitespace
|
||||
|
||||
Our C-style is to use spaces and to use 4 of them per indent level. It's a nice
|
||||
power-of-2 number that looks decent on a wide-screen. We have no more reason
|
||||
than that. We break that rule when we have lines that wrap (macros or function
|
||||
arguments or whatnot). When that happens, we like to indent further to line
|
||||
things up in nice tidy columns.
|
||||
|
||||
```C
|
||||
if (stuff_happened)
|
||||
{
|
||||
do_something();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Case
|
||||
|
||||
- Files - all lower case with underscores.
|
||||
- Variables - all lower case with underscores
|
||||
- Macros - all caps with underscores.
|
||||
- Typedefs - all caps with underscores. (also ends with _T).
|
||||
- Functions - camel cased. Usually named ModuleName_FuncName
|
||||
- Constants and Globals - camel cased.
|
||||
|
||||
|
||||
#### Braces
|
||||
|
||||
The left brace is on the next line after the declaration. The right brace is
|
||||
directly below that. Everything in between in indented one level. If you're
|
||||
catching an error and you have a one-line, go ahead and to it on the same line.
|
||||
|
||||
```C
|
||||
while (blah)
|
||||
{
|
||||
//Like so. Even if only one line, we use braces.
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Comments
|
||||
|
||||
Do you know what we hate? Old-school C block comments. BUT, we're using them
|
||||
anyway. As we mentioned, our goal is to support every compiler we can,
|
||||
especially embedded compilers. There are STILL C compilers out there that only
|
||||
support old-school block comments. So that is what we're using. We apologize. We
|
||||
think they are ugly too.
|
||||
|
||||
|
||||
## Ruby Details
|
||||
|
||||
Is there really such thing as a Ruby coding standard? Ruby is such a free form
|
||||
language, it seems almost sacrilegious to suggest that people should comply to
|
||||
one method! We'll keep it really brief!
|
||||
|
||||
|
||||
#### Whitespace
|
||||
|
||||
Our Ruby style is to use spaces and to use 2 of them per indent level. It's a
|
||||
nice power-of-2 number that really grooves with Ruby's compact style. We have no
|
||||
more reason than that. We break that rule when we have lines that wrap. When
|
||||
that happens, we like to indent further to line things up in nice tidy columns.
|
||||
|
||||
|
||||
#### Case
|
||||
|
||||
- Files - all lower case with underscores.
|
||||
- Variables - all lower case with underscores
|
||||
- Classes, Modules, etc - Camel cased.
|
||||
- Functions - all lower case with underscores
|
||||
- Constants - all upper case with underscores
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
Egad. Really? We use mark down and we like pdf files because they can be made to
|
||||
look nice while still being portable. Good enough?
|
||||
|
||||
|
||||
*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)*
|
BIN
test/vendor/ceedling/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf
vendored
Normal file
BIN
test/vendor/ceedling/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf
vendored
Normal file
Binary file not shown.
779
test/vendor/ceedling/docs/UnityAssertionsReference.md
vendored
Normal file
779
test/vendor/ceedling/docs/UnityAssertionsReference.md
vendored
Normal file
@ -0,0 +1,779 @@
|
||||
# Unity Assertions Reference
|
||||
|
||||
## Background and Overview
|
||||
|
||||
### Super Condensed Version
|
||||
|
||||
- An assertion establishes truth (i.e. boolean True) for a single condition.
|
||||
Upon boolean False, an assertion stops execution and reports the failure.
|
||||
- Unity is mainly a rich collection of assertions and the support to gather up
|
||||
and easily execute those assertions.
|
||||
- The structure of Unity allows you to easily separate test assertions from
|
||||
source code in, well, test code.
|
||||
- Unity's assertions:
|
||||
- Come in many, many flavors to handle different C types and assertion cases.
|
||||
- Use context to provide detailed and helpful failure messages.
|
||||
- Document types, expected values, and basic behavior in your source code for
|
||||
free.
|
||||
|
||||
|
||||
### Unity Is Several Things But Mainly It's Assertions
|
||||
|
||||
One way to think of Unity is simply as a rich collection of assertions you can
|
||||
use to establish whether your source code behaves the way you think it does.
|
||||
Unity provides a framework to easily organize and execute those assertions in
|
||||
test code separate from your source code.
|
||||
|
||||
|
||||
### What's an Assertion?
|
||||
|
||||
At their core, assertions are an establishment of truth - boolean truth. Was this
|
||||
thing equal to that thing? Does that code doohickey have such-and-such property
|
||||
or not? You get the idea. Assertions are executable code (to appreciate the big
|
||||
picture on this read up on the difference between
|
||||
[link:Dynamic Verification and Static Analysis]). A failing assertion stops
|
||||
execution and reports an error through some appropriate I/O channel (e.g.
|
||||
stdout, GUI, file, blinky light).
|
||||
|
||||
Fundamentally, for dynamic verification all you need is a single assertion
|
||||
mechanism. In fact, that's what the [assert() macro in C's standard library](http://en.wikipedia.org/en/wiki/Assert.h)
|
||||
is for. So why not just use it? Well, we can do far better in the reporting
|
||||
department. C's `assert()` is pretty dumb as-is and is particularly poor for
|
||||
handling common data types like arrays, structs, etc. And, without some other
|
||||
support, it's far too tempting to litter source code with C's `assert()`'s. It's
|
||||
generally much cleaner, manageable, and more useful to separate test and source
|
||||
code in the way Unity facilitates.
|
||||
|
||||
|
||||
### Unity's Assertions: Helpful Messages _and_ Free Source Code Documentation
|
||||
|
||||
Asserting a simple truth condition is valuable, but using the context of the
|
||||
assertion is even more valuable. For instance, if you know you're comparing bit
|
||||
flags and not just integers, then why not use that context to give explicit,
|
||||
readable, bit-level feedback when an assertion fails?
|
||||
|
||||
That's what Unity's collection of assertions do - capture context to give you
|
||||
helpful, meaningful assertion failure messages. In fact, the assertions
|
||||
themselves also serve as executable documentation about types and values in your
|
||||
source code. So long as your tests remain current with your source and all those
|
||||
tests pass, you have a detailed, up-to-date view of the intent and mechanisms in
|
||||
your source code. And due to a wondrous mystery, well-tested code usually tends
|
||||
to be well designed code.
|
||||
|
||||
|
||||
## Assertion Conventions and Configurations
|
||||
|
||||
### Naming and Parameter Conventions
|
||||
|
||||
The convention of assertion parameters generally follows this order:
|
||||
|
||||
TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} )
|
||||
|
||||
The very simplest assertion possible uses only a single "actual" parameter (e.g.
|
||||
a simple null check).
|
||||
|
||||
"Actual" is the value being tested and unlike the other parameters in an
|
||||
assertion construction is the only parameter present in all assertion variants.
|
||||
"Modifiers" are masks, ranges, bit flag specifiers, floating point deltas.
|
||||
"Expected" is your expected value (duh) to compare to an "actual" value; it's
|
||||
marked as an optional parameter because some assertions only need a single
|
||||
"actual" parameter (e.g. null check).
|
||||
"Size/count" refers to string lengths, number of array elements, etc.
|
||||
|
||||
Many of Unity's assertions are clear duplications in that the same data type
|
||||
is handled by several assertions. The differences among these are in how failure
|
||||
messages are presented. For instance, a `_HEX` variant of an assertion prints
|
||||
the expected and actual values of that assertion formatted as hexadecimal.
|
||||
|
||||
|
||||
#### TEST_ASSERT_X_MESSAGE Variants
|
||||
|
||||
_All_ assertions are complemented with a variant that includes a simple string
|
||||
message as a final parameter. The string you specify is appended to an assertion
|
||||
failure message in Unity output.
|
||||
|
||||
For brevity, the assertion variants with a message parameter are not listed
|
||||
below. Just tack on `_MESSAGE` as the final component to any assertion name in
|
||||
the reference list below and add a string as the final parameter.
|
||||
|
||||
_Example:_
|
||||
|
||||
TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} )
|
||||
|
||||
becomes messageified like thus...
|
||||
|
||||
TEST_ASSERT_X_MESSAGE( {modifiers}, {expected}, actual, {size/count}, message )
|
||||
|
||||
Notes:
|
||||
- The `_MESSAGE` variants intentionally do not support `printf` style formatting
|
||||
since many embedded projects don't support or avoid `printf` for various reasons.
|
||||
It is possible to use `sprintf` before the assertion to assemble a complex fail
|
||||
message, if necessary.
|
||||
- If you want to output a counter value within an assertion fail message (e.g. from
|
||||
a loop) , building up an array of results and then using one of the `_ARRAY`
|
||||
assertions (see below) might be a handy alternative to `sprintf`.
|
||||
|
||||
|
||||
#### TEST_ASSERT_X_ARRAY Variants
|
||||
|
||||
Unity provides a collection of assertions for arrays containing a variety of
|
||||
types. These are documented in the Array section below. These are almost on par
|
||||
with the `_MESSAGE`variants of Unity's Asserts in that for pretty much any Unity
|
||||
type assertion you can tack on `_ARRAY` and run assertions on an entire block of
|
||||
memory.
|
||||
|
||||
TEST_ASSERT_EQUAL_TYPEX_ARRAY( expected, actual, {size/count} )
|
||||
|
||||
"Expected" is an array itself.
|
||||
"Size/count" is one or two parameters necessary to establish the number of array
|
||||
elements and perhaps the length of elements within the array.
|
||||
|
||||
Notes:
|
||||
- The `_MESSAGE` variant convention still applies here to array assertions. The
|
||||
`_MESSAGE` variants of the `_ARRAY` assertions have names ending with
|
||||
`_ARRAY_MESSAGE`.
|
||||
- Assertions for handling arrays of floating point values are grouped with float
|
||||
and double assertions (see immediately following section).
|
||||
|
||||
|
||||
### TEST_ASSERT_EACH_EQUAL_X Variants
|
||||
|
||||
Unity provides a collection of assertions for arrays containing a variety of
|
||||
types which can be compared to a single value as well. These are documented in
|
||||
the Each Equal section below. these are almost on par with the `_MESSAGE`
|
||||
variants of Unity's Asserts in that for pretty much any Unity type assertion you
|
||||
can inject _EACH_EQUAL and run assertions on an entire block of memory.
|
||||
|
||||
TEST_ASSERT_EACH_EQUAL_TYPEX( expected, actual, {size/count} )
|
||||
|
||||
"Expected" is a single value to compare to.
|
||||
"Actual" is an array where each element will be compared to the expected value.
|
||||
"Size/count" is one of two parameters necessary to establish the number of array
|
||||
elements and perhaps the length of elements within the array.
|
||||
|
||||
Notes:
|
||||
- The `_MESSAGE` variant convention still applies here to Each Equal assertions.
|
||||
- Assertions for handling Each Equal of floating point values are grouped with
|
||||
float and double assertions (see immediately following section).
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
#### Floating Point Support Is Optional
|
||||
|
||||
Support for floating point types is configurable. That is, by defining the
|
||||
appropriate preprocessor symbols, floats and doubles can be individually enabled
|
||||
or disabled in Unity code. This is useful for embedded targets with no floating
|
||||
point math support (i.e. Unity compiles free of errors for fixed point only
|
||||
platforms). See Unity documentation for specifics.
|
||||
|
||||
|
||||
#### Maximum Data Type Width Is Configurable
|
||||
|
||||
Not all targets support 64 bit wide types or even 32 bit wide types. Define the
|
||||
appropriate preprocessor symbols and Unity will omit all operations from
|
||||
compilation that exceed the maximum width of your target. See Unity
|
||||
documentation for specifics.
|
||||
|
||||
|
||||
## The Assertions in All Their Blessed Glory
|
||||
|
||||
### Basic Fail and Ignore
|
||||
|
||||
##### `TEST_FAIL()`
|
||||
|
||||
This fella is most often used in special conditions where your test code is
|
||||
performing logic beyond a simple assertion. That is, in practice, `TEST_FAIL()`
|
||||
will always be found inside a conditional code block.
|
||||
|
||||
_Examples:_
|
||||
- Executing a state machine multiple times that increments a counter your test
|
||||
code then verifies as a final step.
|
||||
- Triggering an exception and verifying it (as in Try / Catch / Throw - see the
|
||||
[CException](https://github.com/ThrowTheSwitch/CException) project).
|
||||
|
||||
##### `TEST_IGNORE()`
|
||||
|
||||
Marks a test case (i.e. function meant to contain test assertions) as ignored.
|
||||
Usually this is employed as a breadcrumb to come back and implement a test case.
|
||||
An ignored test case has effects if other assertions are in the enclosing test
|
||||
case (see Unity documentation for more).
|
||||
|
||||
### Boolean
|
||||
|
||||
##### `TEST_ASSERT (condition)`
|
||||
|
||||
##### `TEST_ASSERT_TRUE (condition)`
|
||||
|
||||
##### `TEST_ASSERT_FALSE (condition)`
|
||||
|
||||
##### `TEST_ASSERT_UNLESS (condition)`
|
||||
|
||||
A simple wording variation on `TEST_ASSERT_FALSE`.The semantics of
|
||||
`TEST_ASSERT_UNLESS` aid readability in certain test constructions or
|
||||
conditional statements.
|
||||
|
||||
##### `TEST_ASSERT_NULL (pointer)`
|
||||
|
||||
##### `TEST_ASSERT_NOT_NULL (pointer)`
|
||||
|
||||
|
||||
### Signed and Unsigned Integers (of all sizes)
|
||||
|
||||
Large integer sizes can be disabled for build targets that do not support them.
|
||||
For example, if your target only supports up to 16 bit types, by defining the
|
||||
appropriate symbols Unity can be configured to omit 32 and 64 bit operations
|
||||
that would break compilation (see Unity documentation for more). Refer to
|
||||
Advanced Asserting later in this document for advice on dealing with other word
|
||||
sizes.
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_INT (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_INT8 (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_INT16 (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_INT32 (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_INT64 (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_NOT_EQUAL (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_UINT (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_UINT8 (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_UINT16 (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_UINT32 (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_UINT64 (expected, actual)`
|
||||
|
||||
|
||||
### Unsigned Integers (of all sizes) in Hexadecimal
|
||||
|
||||
All `_HEX` assertions are identical in function to unsigned integer assertions
|
||||
but produce failure messages with the `expected` and `actual` values formatted
|
||||
in hexadecimal. Unity output is big endian.
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_HEX (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_HEX8 (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_HEX16 (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_HEX32 (expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_HEX64 (expected, actual)`
|
||||
|
||||
|
||||
### Masked and Bit-level Assertions
|
||||
|
||||
Masked and bit-level assertions produce output formatted in hexadecimal. Unity
|
||||
output is big endian.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_BITS (mask, expected, actual)`
|
||||
|
||||
Only compares the masked (i.e. high) bits of `expected` and `actual` parameters.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_BITS_HIGH (mask, actual)`
|
||||
|
||||
Asserts the masked bits of the `actual` parameter are high.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_BITS_LOW (mask, actual)`
|
||||
|
||||
Asserts the masked bits of the `actual` parameter are low.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_BIT_HIGH (bit, actual)`
|
||||
|
||||
Asserts the specified bit of the `actual` parameter is high.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_BIT_LOW (bit, actual)`
|
||||
|
||||
Asserts the specified bit of the `actual` parameter is low.
|
||||
|
||||
### Integer Less Than / Greater Than
|
||||
|
||||
These assertions verify that the `actual` parameter is less than or greater
|
||||
than `threshold` (exclusive). For example, if the threshold value is 0 for the
|
||||
greater than assertion will fail if it is 0 or less.
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_INT (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_INT8 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_INT16 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_INT32 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_UINT (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_UINT8 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_UINT16 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_UINT32 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_HEX8 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_HEX16 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_GREATER_THAN_HEX32 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_INT (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_INT8 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_INT16 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_INT32 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_UINT (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_UINT8 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_UINT16 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_UINT32 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_HEX8 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_HEX16 (threshold, actual)`
|
||||
|
||||
##### `TEST_ASSERT_LESS_THAN_HEX32 (threshold, actual)`
|
||||
|
||||
|
||||
### Integer Ranges (of all sizes)
|
||||
|
||||
These assertions verify that the `expected` parameter is within +/- `delta`
|
||||
(inclusive) of the `actual` parameter. For example, if the expected value is 10
|
||||
and the delta is 3 then the assertion will fail for any value outside the range
|
||||
of 7 - 13.
|
||||
|
||||
##### `TEST_ASSERT_INT_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_INT8_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_INT16_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_INT32_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_INT64_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_UINT_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_UINT8_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_UINT16_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_UINT32_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_UINT64_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_HEX_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_HEX8_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_HEX16_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_HEX32_WITHIN (delta, expected, actual)`
|
||||
|
||||
##### `TEST_ASSERT_HEX64_WITHIN (delta, expected, actual)`
|
||||
|
||||
|
||||
### Structs and Strings
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_PTR (expected, actual)`
|
||||
|
||||
Asserts that the pointers point to the same memory location.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_STRING (expected, actual)`
|
||||
|
||||
Asserts that the null terminated (`'\0'`)strings are identical. If strings are
|
||||
of different lengths or any portion of the strings before their terminators
|
||||
differ, the assertion fails. Two NULL strings (i.e. zero length) are considered
|
||||
equivalent.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_MEMORY (expected, actual, len)`
|
||||
|
||||
Asserts that the contents of the memory specified by the `expected` and `actual`
|
||||
pointers is identical. The size of the memory blocks in bytes is specified by
|
||||
the `len` parameter.
|
||||
|
||||
|
||||
### Arrays
|
||||
|
||||
`expected` and `actual` parameters are both arrays. `num_elements` specifies the
|
||||
number of elements in the arrays to compare.
|
||||
|
||||
`_HEX` assertions produce failure messages with expected and actual array
|
||||
contents formatted in hexadecimal.
|
||||
|
||||
For array of strings comparison behavior, see comments for
|
||||
`TEST_ASSERT_EQUAL_STRING` in the preceding section.
|
||||
|
||||
Assertions fail upon the first element in the compared arrays found not to
|
||||
match. Failure messages specify the array index of the failed comparison.
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_INT_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_INT8_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_INT16_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_INT32_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_INT64_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_UINT_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_UINT8_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_UINT16_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_UINT32_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_UINT64_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_HEX_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_HEX8_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_HEX16_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_HEX32_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_HEX64_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_PTR_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_STRING_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_MEMORY_ARRAY (expected, actual, len, num_elements)`
|
||||
|
||||
`len` is the memory in bytes to be compared at each array element.
|
||||
|
||||
|
||||
### Each Equal (Arrays to Single Value)
|
||||
|
||||
`expected` are single values and `actual` are arrays. `num_elements` specifies
|
||||
the number of elements in the arrays to compare.
|
||||
|
||||
`_HEX` assertions produce failure messages with expected and actual array
|
||||
contents formatted in hexadecimal.
|
||||
|
||||
Assertions fail upon the first element in the compared arrays found not to
|
||||
match. Failure messages specify the array index of the failed comparison.
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_INT (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_INT8 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_INT16 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_INT32 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_INT64 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_UINT (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_UINT8 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_UINT16 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_UINT32 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_UINT64 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_HEX (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_HEX8 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_HEX16 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_HEX32 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_HEX64 (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_PTR (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_STRING (expected, actual, num_elements)`
|
||||
|
||||
#### `TEST_ASSERT_EACH_EQUAL_MEMORY (expected, actual, len, num_elements)`
|
||||
|
||||
`len` is the memory in bytes to be compared at each array element.
|
||||
|
||||
|
||||
### Floating Point (If enabled)
|
||||
|
||||
##### `TEST_ASSERT_FLOAT_WITHIN (delta, expected, actual)`
|
||||
|
||||
Asserts that the `actual` value is within +/- `delta` of the `expected` value.
|
||||
The nature of floating point representation is such that exact evaluations of
|
||||
equality are not guaranteed.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_FLOAT (expected, actual)`
|
||||
|
||||
Asserts that the ?actual?value is "close enough to be considered equal" to the
|
||||
`expected` value. If you are curious about the details, refer to the Advanced
|
||||
Asserting section for more details on this. Omitting a user-specified delta in a
|
||||
floating point assertion is both a shorthand convenience and a requirement of
|
||||
code generation conventions for CMock.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_FLOAT_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
See Array assertion section for details. Note that individual array element
|
||||
float comparisons are executed using T?EST_ASSERT_EQUAL_FLOAT?.That is, user
|
||||
specified delta comparison values requires a custom-implemented floating point
|
||||
array assertion.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_FLOAT_IS_INF (actual)`
|
||||
|
||||
Asserts that `actual` parameter is equivalent to positive infinity floating
|
||||
point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_FLOAT_IS_NEG_INF (actual)`
|
||||
|
||||
Asserts that `actual` parameter is equivalent to negative infinity floating
|
||||
point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_FLOAT_IS_NAN (actual)`
|
||||
|
||||
Asserts that `actual` parameter is a Not A Number floating point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_FLOAT_IS_DETERMINATE (actual)`
|
||||
|
||||
Asserts that ?actual?parameter is a floating point representation usable for
|
||||
mathematical operations. That is, the `actual` parameter is neither positive
|
||||
infinity nor negative infinity nor Not A Number floating point representations.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_FLOAT_IS_NOT_INF (actual)`
|
||||
|
||||
Asserts that `actual` parameter is a value other than positive infinity floating
|
||||
point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_FLOAT_IS_NOT_NEG_INF (actual)`
|
||||
|
||||
Asserts that `actual` parameter is a value other than negative infinity floating
|
||||
point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_FLOAT_IS_NOT_NAN (actual)`
|
||||
|
||||
Asserts that `actual` parameter is a value other than Not A Number floating
|
||||
point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE (actual)`
|
||||
|
||||
Asserts that `actual` parameter is not usable for mathematical operations. That
|
||||
is, the `actual` parameter is either positive infinity or negative infinity or
|
||||
Not A Number floating point representations.
|
||||
|
||||
|
||||
### Double (If enabled)
|
||||
|
||||
##### `TEST_ASSERT_DOUBLE_WITHIN (delta, expected, actual)`
|
||||
|
||||
Asserts that the `actual` value is within +/- `delta` of the `expected` value.
|
||||
The nature of floating point representation is such that exact evaluations of
|
||||
equality are not guaranteed.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_DOUBLE (expected, actual)`
|
||||
|
||||
Asserts that the `actual` value is "close enough to be considered equal" to the
|
||||
`expected` value. If you are curious about the details, refer to the Advanced
|
||||
Asserting section for more details. Omitting a user-specified delta in a
|
||||
floating point assertion is both a shorthand convenience and a requirement of
|
||||
code generation conventions for CMock.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_EQUAL_DOUBLE_ARRAY (expected, actual, num_elements)`
|
||||
|
||||
See Array assertion section for details. Note that individual array element
|
||||
double comparisons are executed using `TEST_ASSERT_EQUAL_DOUBLE`.That is, user
|
||||
specified delta comparison values requires a custom implemented double array
|
||||
assertion.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_DOUBLE_IS_INF (actual)`
|
||||
|
||||
Asserts that `actual` parameter is equivalent to positive infinity floating
|
||||
point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_DOUBLE_IS_NEG_INF (actual)`
|
||||
|
||||
Asserts that `actual` parameter is equivalent to negative infinity floating point
|
||||
representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_DOUBLE_IS_NAN (actual)`
|
||||
|
||||
Asserts that `actual` parameter is a Not A Number floating point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_DOUBLE_IS_DETERMINATE (actual)`
|
||||
|
||||
Asserts that `actual` parameter is a floating point representation usable for
|
||||
mathematical operations. That is, the ?actual?parameter is neither positive
|
||||
infinity nor negative infinity nor Not A Number floating point representations.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_DOUBLE_IS_NOT_INF (actual)`
|
||||
|
||||
Asserts that `actual` parameter is a value other than positive infinity floating
|
||||
point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF (actual)`
|
||||
|
||||
Asserts that `actual` parameter is a value other than negative infinity floating
|
||||
point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_DOUBLE_IS_NOT_NAN (actual)`
|
||||
|
||||
Asserts that `actual` parameter is a value other than Not A Number floating
|
||||
point representation.
|
||||
|
||||
|
||||
##### `TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE (actual)`
|
||||
|
||||
Asserts that `actual` parameter is not usable for mathematical operations. That
|
||||
is, the `actual` parameter is either positive infinity or negative infinity or
|
||||
Not A Number floating point representations.
|
||||
|
||||
|
||||
## Advanced Asserting: Details On Tricky Assertions
|
||||
|
||||
This section helps you understand how to deal with some of the trickier
|
||||
assertion situations you may run into. It will give you a glimpse into some of
|
||||
the under-the-hood details of Unity's assertion mechanisms. If you're one of
|
||||
those people who likes to know what is going on in the background, read on. If
|
||||
not, feel free to ignore the rest of this document until you need it.
|
||||
|
||||
|
||||
### How do the EQUAL assertions work for FLOAT and DOUBLE?
|
||||
|
||||
As you may know, directly checking for equality between a pair of floats or a
|
||||
pair of doubles is sloppy at best and an outright no-no at worst. Floating point
|
||||
values can often be represented in multiple ways, particularly after a series of
|
||||
operations on a value. Initializing a variable to the value of 2.0 is likely to
|
||||
result in a floating point representation of 2 x 20,but a series of
|
||||
mathematical operations might result in a representation of 8 x 2-2
|
||||
that also evaluates to a value of 2. At some point repeated operations cause
|
||||
equality checks to fail.
|
||||
|
||||
So Unity doesn't do direct floating point comparisons for equality. Instead, it
|
||||
checks if two floating point values are "really close." If you leave Unity
|
||||
running with defaults, "really close" means "within a significant bit or two."
|
||||
Under the hood, `TEST_ASSERT_EQUAL_FLOAT` is really `TEST_ASSERT_FLOAT_WITHIN`
|
||||
with the `delta` parameter calculated on the fly. For single precision, delta is
|
||||
the expected value multiplied by 0.00001, producing a very small proportional
|
||||
range around the expected value.
|
||||
|
||||
If you are expecting a value of 20,000.0 the delta is calculated to be 0.2. So
|
||||
any value between 19,999.8 and 20,000.2 will satisfy the equality check. This
|
||||
works out to be roughly a single bit of range for a single-precision number, and
|
||||
that's just about as tight a tolerance as you can reasonably get from a floating
|
||||
point value.
|
||||
|
||||
So what happens when it's zero? Zero - even more than other floating point
|
||||
values - can be represented many different ways. It doesn't matter if you have
|
||||
0 x 20 or 0 x 263.It's still zero, right? Luckily, if you
|
||||
subtract these values from each other, they will always produce a difference of
|
||||
zero, which will still fall between 0 plus or minus a delta of 0. So it still
|
||||
works!
|
||||
|
||||
Double precision floating point numbers use a much smaller multiplier, again
|
||||
approximating a single bit of error.
|
||||
|
||||
If you don't like these ranges and you want to make your floating point equality
|
||||
assertions less strict, you can change these multipliers to whatever you like by
|
||||
defining UNITY_FLOAT_PRECISION and UNITY_DOUBLE_PRECISION. See Unity
|
||||
documentation for more.
|
||||
|
||||
|
||||
### How do we deal with targets with non-standard int sizes?
|
||||
|
||||
It's "fun" that C is a standard where something as fundamental as an integer
|
||||
varies by target. According to the C standard, an `int` is to be the target's
|
||||
natural register size, and it should be at least 16-bits and a multiple of a
|
||||
byte. It also guarantees an order of sizes:
|
||||
|
||||
```C
|
||||
char <= short <= int <= long <= long long
|
||||
```
|
||||
|
||||
Most often, `int` is 32-bits. In many cases in the embedded world, `int` is
|
||||
16-bits. There are rare microcontrollers out there that have 24-bit integers,
|
||||
and this remains perfectly standard C.
|
||||
|
||||
To make things even more interesting, there are compilers and targets out there
|
||||
that have a hard choice to make. What if their natural register size is 10-bits
|
||||
or 12-bits? Clearly they can't fulfill _both_ the requirement to be at least
|
||||
16-bits AND the requirement to match the natural register size. In these
|
||||
situations, they often choose the natural register size, leaving us with
|
||||
something like this:
|
||||
|
||||
```C
|
||||
char (8 bit) <= short (12 bit) <= int (12 bit) <= long (16 bit)
|
||||
```
|
||||
|
||||
Um... yikes. It's obviously breaking a rule or two... but they had to break SOME
|
||||
rules, so they made a choice.
|
||||
|
||||
When the C99 standard rolled around, it introduced alternate standard-size types.
|
||||
It also introduced macros for pulling in MIN/MAX values for your integer types.
|
||||
It's glorious! Unfortunately, many embedded compilers can't be relied upon to
|
||||
use the C99 types (Sometimes because they have weird register sizes as described
|
||||
above. Sometimes because they don't feel like it?).
|
||||
|
||||
A goal of Unity from the beginning was to support every combination of
|
||||
microcontroller or microprocessor and C compiler. Over time, we've gotten really
|
||||
close to this. There are a few tricks that you should be aware of, though, if
|
||||
you're going to do this effectively on some of these more idiosyncratic targets.
|
||||
|
||||
First, when setting up Unity for a new target, you're going to want to pay
|
||||
special attention to the macros for automatically detecting types
|
||||
(where available) or manually configuring them yourself. You can get information
|
||||
on both of these in Unity's documentation.
|
||||
|
||||
What about the times where you suddenly need to deal with something odd, like a
|
||||
24-bit `int`? The simplest solution is to use the next size up. If you have a
|
||||
24-bit `int`, configure Unity to use 32-bit integers. If you have a 12-bit
|
||||
`int`, configure Unity to use 16 bits. There are two ways this is going to
|
||||
affect you:
|
||||
|
||||
1. When Unity displays errors for you, it's going to pad the upper unused bits
|
||||
with zeros.
|
||||
2. You're going to have to be careful of assertions that perform signed
|
||||
operations, particularly `TEST_ASSERT_INT_WITHIN`.Such assertions might wrap
|
||||
your `int` in the wrong place, and you could experience false failures. You can
|
||||
always back down to a simple `TEST_ASSERT` and do the operations yourself.
|
||||
|
||||
|
||||
*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)*
|
433
test/vendor/ceedling/docs/UnityConfigurationGuide.md
vendored
Normal file
433
test/vendor/ceedling/docs/UnityConfigurationGuide.md
vendored
Normal file
@ -0,0 +1,433 @@
|
||||
# Unity Configuration Guide
|
||||
|
||||
## C Standards, Compilers and Microcontrollers
|
||||
|
||||
The embedded software world contains its challenges. Compilers support different
|
||||
revisions of the C Standard. They ignore requirements in places, sometimes to
|
||||
make the language more usable in some special regard. Sometimes it's to simplify
|
||||
their support. Sometimes it's due to specific quirks of the microcontroller they
|
||||
are targeting. Simulators add another dimension to this menagerie.
|
||||
|
||||
Unity is designed to run on almost anything that is targeted by a C compiler. It
|
||||
would be awesome if this could be done with zero configuration. While there are
|
||||
some targets that come close to this dream, it is sadly not universal. It is
|
||||
likely that you are going to need at least a couple of the configuration options
|
||||
described in this document.
|
||||
|
||||
All of Unity's configuration options are `#defines`. Most of these are simple
|
||||
definitions. A couple are macros with arguments. They live inside the
|
||||
unity_internals.h header file. We don't necessarily recommend opening that file
|
||||
unless you really need to. That file is proof that a cross-platform library is
|
||||
challenging to build. From a more positive perspective, it is also proof that a
|
||||
great deal of complexity can be centralized primarily to one place to
|
||||
provide a more consistent and simple experience elsewhere.
|
||||
|
||||
|
||||
### Using These Options
|
||||
|
||||
It doesn't matter if you're using a target-specific compiler and a simulator or
|
||||
a native compiler. In either case, you've got a couple choices for configuring
|
||||
these options:
|
||||
|
||||
1. Because these options are specified via C defines, you can pass most of these
|
||||
options to your compiler through command line compiler flags. Even if you're
|
||||
using an embedded target that forces you to use their overbearing IDE for all
|
||||
configuration, there will be a place somewhere in your project to configure
|
||||
defines for your compiler.
|
||||
2. You can create a custom `unity_config.h` configuration file (present in your
|
||||
toolchain's search paths). In this file, you will list definitions and macros
|
||||
specific to your target. All you must do is define `UNITY_INCLUDE_CONFIG_H` and
|
||||
Unity will rely on `unity_config.h` for any further definitions it may need.
|
||||
|
||||
|
||||
## The Options
|
||||
|
||||
### Integer Types
|
||||
|
||||
If you've been a C developer for long, you probably already know that C's
|
||||
concept of an integer varies from target to target. The C Standard has rules
|
||||
about the `int` matching the register size of the target microprocessor. It has
|
||||
rules about the `int` and how its size relates to other integer types. An `int`
|
||||
on one target might be 16 bits while on another target it might be 64. There are
|
||||
more specific types in compilers compliant with C99 or later, but that's
|
||||
certainly not every compiler you are likely to encounter. Therefore, Unity has a
|
||||
number of features for helping to adjust itself to match your required integer
|
||||
sizes. It starts off by trying to do it automatically.
|
||||
|
||||
|
||||
##### `UNITY_EXCLUDE_STDINT_H`
|
||||
|
||||
The first thing that Unity does to guess your types is check `stdint.h`.
|
||||
This file includes defines like `UINT_MAX` that Unity can use to
|
||||
learn a lot about your system. It's possible you don't want it to do this
|
||||
(um. why not?) or (more likely) it's possible that your system doesn't
|
||||
support `stdint.h`. If that's the case, you're going to want to define this.
|
||||
That way, Unity will know to skip the inclusion of this file and you won't
|
||||
be left with a compiler error.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_EXCLUDE_STDINT_H
|
||||
|
||||
|
||||
##### `UNITY_EXCLUDE_LIMITS_H`
|
||||
|
||||
The second attempt to guess your types is to check `limits.h`. Some compilers
|
||||
that don't support `stdint.h` could include `limits.h` instead. If you don't
|
||||
want Unity to check this file either, define this to make it skip the inclusion.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_EXCLUDE_LIMITS_H
|
||||
|
||||
|
||||
If you've disabled both of the automatic options above, you're going to have to
|
||||
do the configuration yourself. Don't worry. Even this isn't too bad... there are
|
||||
just a handful of defines that you are going to specify if you don't like the
|
||||
defaults.
|
||||
|
||||
|
||||
##### `UNITY_INT_WIDTH`
|
||||
|
||||
Define this to be the number of bits an `int` takes up on your system. The
|
||||
default, if not autodetected, is 32 bits.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_INT_WIDTH 16
|
||||
|
||||
|
||||
##### `UNITY_LONG_WIDTH`
|
||||
|
||||
Define this to be the number of bits a `long` takes up on your system. The
|
||||
default, if not autodetected, is 32 bits. This is used to figure out what kind
|
||||
of 64-bit support your system can handle. Does it need to specify a `long` or a
|
||||
`long long` to get a 64-bit value. On 16-bit systems, this option is going to be
|
||||
ignored.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_LONG_WIDTH 16
|
||||
|
||||
|
||||
##### `UNITY_POINTER_WIDTH`
|
||||
|
||||
Define this to be the number of bits a pointer takes up on your system. The
|
||||
default, if not autodetected, is 32-bits. If you're getting ugly compiler
|
||||
warnings about casting from pointers, this is the one to look at.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_POINTER_WIDTH 64
|
||||
|
||||
|
||||
##### `UNITY_SUPPORT_64`
|
||||
|
||||
Unity will automatically include 64-bit support if it auto-detects it, or if
|
||||
your `int`, `long`, or pointer widths are greater than 32-bits. Define this to
|
||||
enable 64-bit support if none of the other options already did it for you. There
|
||||
can be a significant size and speed impact to enabling 64-bit support on small
|
||||
targets, so don't define it if you don't need it.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_SUPPORT_64
|
||||
|
||||
|
||||
### Floating Point Types
|
||||
|
||||
In the embedded world, it's not uncommon for targets to have no support for
|
||||
floating point operations at all or to have support that is limited to only
|
||||
single precision. We are able to guess integer sizes on the fly because integers
|
||||
are always available in at least one size. Floating point, on the other hand, is
|
||||
sometimes not available at all. Trying to include `float.h` on these platforms
|
||||
would result in an error. This leaves manual configuration as the only option.
|
||||
|
||||
|
||||
##### `UNITY_INCLUDE_FLOAT`
|
||||
|
||||
##### `UNITY_EXCLUDE_FLOAT`
|
||||
|
||||
##### `UNITY_INCLUDE_DOUBLE`
|
||||
|
||||
##### `UNITY_EXCLUDE_DOUBLE`
|
||||
|
||||
By default, Unity guesses that you will want single precision floating point
|
||||
support, but not double precision. It's easy to change either of these using the
|
||||
include and exclude options here. You may include neither, either, or both, as
|
||||
suits your needs. For features that are enabled, the following floating point
|
||||
options also become available.
|
||||
|
||||
_Example:_
|
||||
|
||||
//what manner of strange processor is this?
|
||||
#define UNITY_EXCLUDE_FLOAT
|
||||
#define UNITY_INCLUDE_DOUBLE
|
||||
|
||||
|
||||
##### `UNITY_EXCLUDE_FLOAT_PRINT`
|
||||
|
||||
Unity aims for as small of a footprint as possible and avoids most standard
|
||||
library calls (some embedded platforms don’t have a standard library!). Because
|
||||
of this, its routines for printing integer values are minimalist and hand-coded.
|
||||
Therefore, the display of floating point values during a failure are optional.
|
||||
By default, Unity will print the actual results of floating point assertion
|
||||
failure (e.g. ”Expected 4.56 Was 4.68”). To not include this extra support, you
|
||||
can use this define to instead respond to a failed assertion with a message like
|
||||
”Values Not Within Delta”. If you would like verbose failure messages for floating
|
||||
point assertions, use these options to give more explicit failure messages.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_EXCLUDE_FLOAT_PRINT
|
||||
|
||||
|
||||
##### `UNITY_FLOAT_TYPE`
|
||||
|
||||
If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C
|
||||
floats. If your compiler supports a specialty floating point type, you can
|
||||
always override this behavior by using this definition.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_FLOAT_TYPE float16_t
|
||||
|
||||
|
||||
##### `UNITY_DOUBLE_TYPE`
|
||||
|
||||
If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard C
|
||||
doubles. If you would like to change this, you can specify something else by
|
||||
using this option. For example, defining `UNITY_DOUBLE_TYPE` to `long double`
|
||||
could enable gargantuan floating point types on your 64-bit processor instead of
|
||||
the standard `double`.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_DOUBLE_TYPE long double
|
||||
|
||||
|
||||
##### `UNITY_FLOAT_PRECISION`
|
||||
|
||||
##### `UNITY_DOUBLE_PRECISION`
|
||||
|
||||
If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as
|
||||
documented in the big daddy Unity Assertion Guide, you will learn that they are
|
||||
not really asserting that two values are equal but rather that two values are
|
||||
"close enough" to equal. "Close enough" is controlled by these precision
|
||||
configuration options. If you are working with 32-bit floats and/or 64-bit
|
||||
doubles (the normal on most processors), you should have no need to change these
|
||||
options. They are both set to give you approximately 1 significant bit in either
|
||||
direction. The float precision is 0.00001 while the double is 10-12.
|
||||
For further details on how this works, see the appendix of the Unity Assertion
|
||||
Guide.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_FLOAT_PRECISION 0.001f
|
||||
|
||||
|
||||
### Toolset Customization
|
||||
|
||||
In addition to the options listed above, there are a number of other options
|
||||
which will come in handy to customize Unity's behavior for your specific
|
||||
toolchain. It is possible that you may not need to touch any of these... but
|
||||
certain platforms, particularly those running in simulators, may need to jump
|
||||
through extra hoops to run properly. These macros will help in those
|
||||
situations.
|
||||
|
||||
|
||||
##### `UNITY_OUTPUT_CHAR(a)`
|
||||
|
||||
##### `UNITY_OUTPUT_FLUSH()`
|
||||
|
||||
##### `UNITY_OUTPUT_START()`
|
||||
|
||||
##### `UNITY_OUTPUT_COMPLETE()`
|
||||
|
||||
By default, Unity prints its results to `stdout` as it runs. This works
|
||||
perfectly fine in most situations where you are using a native compiler for
|
||||
testing. It works on some simulators as well so long as they have `stdout`
|
||||
routed back to the command line. There are times, however, where the simulator
|
||||
will lack support for dumping results or you will want to route results
|
||||
elsewhere for other reasons. In these cases, you should define the
|
||||
`UNITY_OUTPUT_CHAR` macro. This macro accepts a single character at a time (as
|
||||
an `int`, since this is the parameter type of the standard C `putchar` function
|
||||
most commonly used). You may replace this with whatever function call you like.
|
||||
|
||||
_Example:_
|
||||
Say you are forced to run your test suite on an embedded processor with no
|
||||
`stdout` option. You decide to route your test result output to a custom serial
|
||||
`RS232_putc()` function you wrote like thus:
|
||||
#include "RS232_header.h"
|
||||
...
|
||||
#define UNITY_OUTPUT_CHAR(a) RS232_putc(a)
|
||||
#define UNITY_OUTPUT_START() RS232_config(115200,1,8,0)
|
||||
#define UNITY_OUTPUT_FLUSH() RS232_flush()
|
||||
#define UNITY_OUTPUT_COMPLETE() RS232_close()
|
||||
|
||||
_Note:_
|
||||
`UNITY_OUTPUT_FLUSH()` can be set to the standard out flush function simply by
|
||||
specifying `UNITY_USE_FLUSH_STDOUT`. No other defines are required.
|
||||
|
||||
|
||||
##### `UNITY_WEAK_ATTRIBUTE`
|
||||
|
||||
##### `UNITY_WEAK_PRAGMA`
|
||||
|
||||
##### `UNITY_NO_WEAK`
|
||||
|
||||
For some targets, Unity can make the otherwise required setUp() and tearDown()
|
||||
functions optional. This is a nice convenience for test writers since setUp and
|
||||
tearDown don’t often actually do anything. If you’re using gcc or clang, this
|
||||
option is automatically defined for you. Other compilers can also support this
|
||||
behavior, if they support a C feature called weak functions. A weak function is
|
||||
a function that is compiled into your executable unless a non-weak version of
|
||||
the same function is defined elsewhere. If a non-weak version is found, the weak
|
||||
version is ignored as if it never existed. If your compiler supports this feature,
|
||||
you can let Unity know by defining UNITY_WEAK_ATTRIBUTE or UNITY_WEAK_PRAGMA as
|
||||
the function attributes that would need to be applied to identify a function as
|
||||
weak. If your compiler lacks support for weak functions, you will always need to
|
||||
define setUp and tearDown functions (though they can be and often will be just
|
||||
empty). You can also force Unity to NOT use weak functions by defining
|
||||
UNITY_NO_WEAK. The most common options for this feature are:
|
||||
|
||||
_Example:_
|
||||
#define UNITY_WEAK_ATTRIBUTE weak
|
||||
#define UNITY_WEAK_ATTRIBUTE __attribute__((weak))
|
||||
#define UNITY_WEAK_PRAGMA
|
||||
#define UNITY_NO_WEAK
|
||||
|
||||
|
||||
##### `UNITY_PTR_ATTRIBUTE`
|
||||
|
||||
Some compilers require a custom attribute to be assigned to pointers, like
|
||||
`near` or `far`. In these cases, you can give Unity a safe default for these by
|
||||
defining this option with the attribute you would like.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_PTR_ATTRIBUTE __attribute__((far))
|
||||
#define UNITY_PTR_ATTRIBUTE near
|
||||
|
||||
|
||||
##### `UNITY_PRINT_EOL`
|
||||
|
||||
By default, Unity outputs \n at the end of each line of output. This is easy
|
||||
to parse by the scripts, by Ceedling, etc, but it might not be ideal for YOUR
|
||||
system. Feel free to override this and to make it whatever you wish.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_PRINT_EOL { UNITY_OUTPUT_CHAR('\r'); UNITY_OUTPUT_CHAR('\n') }
|
||||
|
||||
|
||||
|
||||
##### `UNITY_EXCLUDE_DETAILS`
|
||||
|
||||
This is an option for if you absolutely must squeeze every byte of memory out of
|
||||
your system. Unity stores a set of internal scratchpads which are used to pass
|
||||
extra detail information around. It's used by systems like CMock in order to
|
||||
report which function or argument flagged an error. If you're not using CMock and
|
||||
you're not using these details for other things, then you can exclude them.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_EXCLUDE_DETAILS
|
||||
|
||||
|
||||
|
||||
##### `UNITY_EXCLUDE_SETJMP`
|
||||
|
||||
If your embedded system doesn't support the standard library setjmp, you can
|
||||
exclude Unity's reliance on this by using this define. This dropped dependence
|
||||
comes at a price, though. You will be unable to use custom helper functions for
|
||||
your tests, and you will be unable to use tools like CMock. Very likely, if your
|
||||
compiler doesn't support setjmp, you wouldn't have had the memory space for those
|
||||
things anyway, though... so this option exists for those situations.
|
||||
|
||||
_Example:_
|
||||
#define UNITY_EXCLUDE_SETJMP
|
||||
|
||||
##### `UNITY_OUTPUT_COLOR`
|
||||
|
||||
If you want to add color using ANSI escape codes you can use this define.
|
||||
t
|
||||
_Example:_
|
||||
#define UNITY_OUTPUT_COLOR
|
||||
|
||||
|
||||
|
||||
## Getting Into The Guts
|
||||
|
||||
There will be cases where the options above aren't quite going to get everything
|
||||
perfect. They are likely sufficient for any situation where you are compiling
|
||||
and executing your tests with a native toolchain (e.g. clang on Mac). These
|
||||
options may even get you through the majority of cases encountered in working
|
||||
with a target simulator run from your local command line. But especially if you
|
||||
must run your test suite on your target hardware, your Unity configuration will
|
||||
require special help. This special help will usually reside in one of two
|
||||
places: the `main()` function or the `RUN_TEST` macro. Let's look at how these
|
||||
work.
|
||||
|
||||
|
||||
##### `main()`
|
||||
|
||||
Each test module is compiled and run on its own, separate from the other test
|
||||
files in your project. Each test file, therefore, has a `main` function. This
|
||||
`main` function will need to contain whatever code is necessary to initialize
|
||||
your system to a workable state. This is particularly true for situations where
|
||||
you must set up a memory map or initialize a communication channel for the
|
||||
output of your test results.
|
||||
|
||||
A simple main function looks something like this:
|
||||
|
||||
int main(void) {
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_TheFirst);
|
||||
RUN_TEST(test_TheSecond);
|
||||
RUN_TEST(test_TheThird);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
You can see that our main function doesn't bother taking any arguments. For our
|
||||
most barebones case, we'll never have arguments because we just run all the
|
||||
tests each time. Instead, we start by calling `UNITY_BEGIN`. We run each test
|
||||
(in whatever order we wish). Finally, we call `UNITY_END`, returning its return
|
||||
value (which is the total number of failures).
|
||||
|
||||
It should be easy to see that you can add code before any test cases are run or
|
||||
after all the test cases have completed. This allows you to do any needed
|
||||
system-wide setup or teardown that might be required for your special
|
||||
circumstances.
|
||||
|
||||
|
||||
##### `RUN_TEST`
|
||||
|
||||
The `RUN_TEST` macro is called with each test case function. Its job is to
|
||||
perform whatever setup and teardown is necessary for executing a single test
|
||||
case function. This includes catching failures, calling the test module's
|
||||
`setUp()` and `tearDown()` functions, and calling `UnityConcludeTest()`. If
|
||||
using CMock or test coverage, there will be additional stubs in use here. A
|
||||
simple minimalist RUN_TEST macro looks something like this:
|
||||
|
||||
#define RUN_TEST(testfunc) \
|
||||
UNITY_NEW_TEST(#testfunc) \
|
||||
if (TEST_PROTECT()) { \
|
||||
setUp(); \
|
||||
testfunc(); \
|
||||
} \
|
||||
if (TEST_PROTECT() && (!TEST_IS_IGNORED)) \
|
||||
tearDown(); \
|
||||
UnityConcludeTest();
|
||||
|
||||
So that's quite a macro, huh? It gives you a glimpse of what kind of stuff Unity
|
||||
has to deal with for every single test case. For each test case, we declare that
|
||||
it is a new test. Then we run `setUp` and our test function. These are run
|
||||
within a `TEST_PROTECT` block, the function of which is to handle failures that
|
||||
occur during the test. Then, assuming our test is still running and hasn't been
|
||||
ignored, we run `tearDown`. No matter what, our last step is to conclude this
|
||||
test before moving on to the next.
|
||||
|
||||
Let's say you need to add a call to `fsync` to force all of your output data to
|
||||
flush to a file after each test. You could easily insert this after your
|
||||
`UnityConcludeTest` call. Maybe you want to write an xml tag before and after
|
||||
each result set. Again, you could do this by adding lines to this macro. Updates
|
||||
to this macro are for the occasions when you need an action before or after
|
||||
every single test case throughout your entire suite of tests.
|
||||
|
||||
|
||||
## Happy Porting
|
||||
|
||||
The defines and macros in this guide should help you port Unity to just about
|
||||
any C target we can imagine. If you run into a snag or two, don't be afraid of
|
||||
asking for help on the forums. We love a good challenge!
|
||||
|
||||
|
||||
*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)*
|
192
test/vendor/ceedling/docs/UnityGettingStartedGuide.md
vendored
Normal file
192
test/vendor/ceedling/docs/UnityGettingStartedGuide.md
vendored
Normal file
@ -0,0 +1,192 @@
|
||||
# Unity - Getting Started
|
||||
|
||||
## Welcome
|
||||
|
||||
Congratulations. You're now the proud owner of your very own pile of bits! What
|
||||
are you going to do with all these ones and zeros? This document should be able
|
||||
to help you decide just that.
|
||||
|
||||
Unity is a unit test framework. The goal has been to keep it small and
|
||||
functional. The core Unity test framework is three files: a single C file and a
|
||||
couple header files. These team up to provide functions and macros to make
|
||||
testing easier.
|
||||
|
||||
Unity was designed to be cross-platform. It works hard to stick with C standards
|
||||
while still providing support for the many embedded C compilers that bend the
|
||||
rules. Unity has been used with many compilers, including GCC, IAR, Clang,
|
||||
Green Hills, Microchip, and MS Visual Studio. It's not much work to get it to
|
||||
work with a new target.
|
||||
|
||||
|
||||
### Overview of the Documents
|
||||
|
||||
#### Unity Assertions reference
|
||||
|
||||
This document will guide you through all the assertion options provided by
|
||||
Unity. This is going to be your unit testing bread and butter. You'll spend more
|
||||
time with assertions than any other part of Unity.
|
||||
|
||||
|
||||
#### Unity Assertions Cheat Sheet
|
||||
|
||||
This document contains an abridged summary of the assertions described in the
|
||||
previous document. It's perfect for printing and referencing while you
|
||||
familiarize yourself with Unity's options.
|
||||
|
||||
|
||||
#### Unity Configuration Guide
|
||||
|
||||
This document is the one to reference when you are going to use Unity with a new
|
||||
target or compiler. It'll guide you through the configuration options and will
|
||||
help you customize your testing experience to meet your needs.
|
||||
|
||||
|
||||
#### Unity Helper Scripts
|
||||
|
||||
This document describes the helper scripts that are available for simplifying
|
||||
your testing workflow. It describes the collection of optional Ruby scripts
|
||||
included in the auto directory of your Unity installation. Neither Ruby nor
|
||||
these scripts are necessary for using Unity. They are provided as a convenience
|
||||
for those who wish to use them.
|
||||
|
||||
|
||||
#### Unity License
|
||||
|
||||
What's an open source project without a license file? This brief document
|
||||
describes the terms you're agreeing to when you use this software. Basically, we
|
||||
want it to be useful to you in whatever context you want to use it, but please
|
||||
don't blame us if you run into problems.
|
||||
|
||||
|
||||
### Overview of the Folders
|
||||
|
||||
If you have obtained Unity through Github or something similar, you might be
|
||||
surprised by just how much stuff you suddenly have staring you in the face.
|
||||
Don't worry, Unity itself is very small. The rest of it is just there to make
|
||||
your life easier. You can ignore it or use it at your convenience. Here's an
|
||||
overview of everything in the project.
|
||||
|
||||
- `src` - This is the code you care about! This folder contains a C file and two
|
||||
header files. These three files _are_ Unity.
|
||||
- `docs` - You're reading this document, so it's possible you have found your way
|
||||
into this folder already. This is where all the handy documentation can be
|
||||
found.
|
||||
- `examples` - This contains a few examples of using Unity.
|
||||
- `extras` - These are optional add ons to Unity that are not part of the core
|
||||
project. If you've reached us through James Grenning's book, you're going to
|
||||
want to look here.
|
||||
- `test` - This is how Unity and its scripts are all tested. If you're just using
|
||||
Unity, you'll likely never need to go in here. If you are the lucky team member
|
||||
who gets to port Unity to a new toolchain, this is a good place to verify
|
||||
everything is configured properly.
|
||||
- `auto` - Here you will find helpful Ruby scripts for simplifying your test
|
||||
workflow. They are purely optional and are not required to make use of Unity.
|
||||
|
||||
|
||||
## How to Create A Test File
|
||||
|
||||
Test files are C files. Most often you will create a single test file for each C
|
||||
module that you want to test. The test file should include unity.h and the
|
||||
header for your C module to be tested.
|
||||
|
||||
Next, a test file will include a `setUp()` and `tearDown()` function. The setUp
|
||||
function can contain anything you would like to run before each test. The
|
||||
tearDown function can contain anything you would like to run after each test.
|
||||
Both functions accept no arguments and return nothing. You may leave either or
|
||||
both of these blank if you have no need for them. If you're using a compiler
|
||||
that is configured to make these functions optional, you may leave them off
|
||||
completely. Not sure? Give it a try. If you compiler complains that it can't
|
||||
find setUp or tearDown when it links, you'll know you need to at least include
|
||||
an empty function for these.
|
||||
|
||||
The majority of the file will be a series of test functions. Test functions
|
||||
follow the convention of starting with the word "test_" or "spec_". You don't HAVE
|
||||
to name them this way, but it makes it clear what functions are tests for other
|
||||
developers. Also, the automated scripts that come with Unity or Ceedling will default
|
||||
to looking for test functions to be prefixed this way. Test functions take no arguments
|
||||
and return nothing. All test accounting is handled internally in Unity.
|
||||
|
||||
Finally, at the bottom of your test file, you will write a `main()` function.
|
||||
This function will call `UNITY_BEGIN()`, then `RUN_TEST` for each test, and
|
||||
finally `UNITY_END()`.This is what will actually trigger each of those test
|
||||
functions to run, so it is important that each function gets its own `RUN_TEST`
|
||||
call.
|
||||
|
||||
Remembering to add each test to the main function can get to be tedious. If you
|
||||
enjoy using helper scripts in your build process, you might consider making use
|
||||
of our handy generate_test_runner.rb script. This will create the main function
|
||||
and all the calls for you, assuming that you have followed the suggested naming
|
||||
conventions. In this case, there is no need for you to include the main function
|
||||
in your test file at all.
|
||||
|
||||
When you're done, your test file will look something like this:
|
||||
|
||||
```C
|
||||
#include "unity.h"
|
||||
#include "file_to_test.h"
|
||||
|
||||
void setUp(void) {
|
||||
// set stuff up here
|
||||
}
|
||||
|
||||
void tearDown(void) {
|
||||
// clean stuff up here
|
||||
}
|
||||
|
||||
void test_function_should_doBlahAndBlah(void) {
|
||||
//test stuff
|
||||
}
|
||||
|
||||
void test_function_should_doAlsoDoBlah(void) {
|
||||
//more test stuff
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_function_should_doBlahAndBlah);
|
||||
RUN_TEST(test_function_should_doAlsoDoBlah);
|
||||
return UNITY_END();
|
||||
}
|
||||
```
|
||||
|
||||
It's possible that you will need more customization than this, eventually.
|
||||
For that sort of thing, you're going to want to look at the configuration guide.
|
||||
This should be enough to get you going, though.
|
||||
|
||||
|
||||
## How to Build and Run A Test File
|
||||
|
||||
This is the single biggest challenge to picking up a new unit testing framework,
|
||||
at least in a language like C or C++. These languages are REALLY good at getting
|
||||
you "close to the metal" (why is the phrase metal? Wouldn't it be more accurate
|
||||
to say "close to the silicon"?). While this feature is usually a good thing, it
|
||||
can make testing more challenging.
|
||||
|
||||
You have two really good options for toolchains. Depending on where you're
|
||||
coming from, it might surprise you that neither of these options is running the
|
||||
unit tests on your hardware.
|
||||
There are many reasons for this, but here's a short version:
|
||||
- On hardware, you have too many constraints (processing power, memory, etc),
|
||||
- On hardware, you don't have complete control over all registers,
|
||||
- On hardware, unit testing is more challenging,
|
||||
- Unit testing isn't System testing. Keep them separate.
|
||||
|
||||
Instead of running your tests on your actual hardware, most developers choose to
|
||||
develop them as native applications (using gcc or MSVC for example) or as
|
||||
applications running on a simulator. Either is a good option. Native apps have
|
||||
the advantages of being faster and easier to set up. Simulator apps have the
|
||||
advantage of working with the same compiler as your target application. The
|
||||
options for configuring these are discussed in the configuration guide.
|
||||
|
||||
To get either to work, you might need to make a few changes to the file
|
||||
containing your register set (discussed later).
|
||||
|
||||
In either case, a test is built by linking unity, the test file, and the C
|
||||
file(s) being tested. These files create an executable which can be run as the
|
||||
test set for that module. Then, this process is repeated for the next test file.
|
||||
This flexibility of separating tests into individual executables allows us to
|
||||
much more thoroughly unit test our system and it keeps all the test code out of
|
||||
our final release!
|
||||
|
||||
|
||||
*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)*
|
260
test/vendor/ceedling/docs/UnityHelperScriptsGuide.md
vendored
Normal file
260
test/vendor/ceedling/docs/UnityHelperScriptsGuide.md
vendored
Normal file
@ -0,0 +1,260 @@
|
||||
# Unity Helper Scripts
|
||||
|
||||
## With a Little Help From Our Friends
|
||||
|
||||
Sometimes what it takes to be a really efficient C programmer is a little non-C.
|
||||
The Unity project includes a couple of Ruby scripts for making your life just a tad
|
||||
easier. They are completely optional. If you choose to use them, you'll need a
|
||||
copy of Ruby, of course. Just install whatever the latest version is, and it is
|
||||
likely to work. You can find Ruby at [ruby-lang.org](https://ruby-labg.org/).
|
||||
|
||||
|
||||
### `generate_test_runner.rb`
|
||||
|
||||
Are you tired of creating your own `main` function in your test file? Do you
|
||||
keep forgetting to add a `RUN_TEST` call when you add a new test case to your
|
||||
suite? Do you want to use CMock or other fancy add-ons but don't want to figure
|
||||
out how to create your own `RUN_TEST` macro?
|
||||
|
||||
Well then we have the perfect script for you!
|
||||
|
||||
The `generate_test_runner` script processes a given test file and automatically
|
||||
creates a separate test runner file that includes ?main?to execute the test
|
||||
cases within the scanned test file. All you do then is add the generated runner
|
||||
to your list of files to be compiled and linked, and presto you're done!
|
||||
|
||||
This script searches your test file for void function signatures having a
|
||||
function name beginning with "test" or "spec". It treats each of these
|
||||
functions as a test case and builds up a test suite of them. For example, the
|
||||
following includes three test cases:
|
||||
|
||||
```C
|
||||
void testVerifyThatUnityIsAwesomeAndWillMakeYourLifeEasier(void)
|
||||
{
|
||||
ASSERT_TRUE(1);
|
||||
}
|
||||
void test_FunctionName_should_WorkProperlyAndReturn8(void) {
|
||||
ASSERT_EQUAL_INT(8, FunctionName());
|
||||
}
|
||||
void spec_Function_should_DoWhatItIsSupposedToDo(void) {
|
||||
ASSERT_NOT_NULL(Function(5));
|
||||
}
|
||||
```
|
||||
|
||||
You can run this script a couple of ways. The first is from the command line:
|
||||
|
||||
```Shell
|
||||
ruby generate_test_runner.rb TestFile.c NameOfRunner.c
|
||||
```
|
||||
|
||||
Alternatively, if you include only the test file parameter, the script will copy
|
||||
the name of the test file and automatically append "_Runner" to the name of the
|
||||
generated file. The example immediately below will create TestFile_Runner.c.
|
||||
|
||||
```Shell
|
||||
ruby generate_test_runner.rb TestFile.c
|
||||
```
|
||||
|
||||
You can also add a [YAML](http://www.yaml.org/) file to configure extra options.
|
||||
Conveniently, this YAML file is of the same format as that used by Unity and
|
||||
CMock. So if you are using YAML files already, you can simply pass the very same
|
||||
file into the generator script.
|
||||
|
||||
```Shell
|
||||
ruby generate_test_runner.rb TestFile.c my_config.yml
|
||||
```
|
||||
|
||||
The contents of the YAML file `my_config.yml` could look something like the
|
||||
example below. If you're wondering what some of these options do, you're going
|
||||
to love the next section of this document.
|
||||
|
||||
```YAML
|
||||
:unity:
|
||||
:includes:
|
||||
- stdio.h
|
||||
- microdefs.h
|
||||
:cexception: 1
|
||||
:suit_setup: "blah = malloc(1024);"
|
||||
:suite_teardown: "free(blah);"
|
||||
```
|
||||
|
||||
If you would like to force your generated test runner to include one or more
|
||||
header files, you can just include those at the command line too. Just make sure
|
||||
these are _after_ the YAML file, if you are using one:
|
||||
|
||||
```Shell
|
||||
ruby generate_test_runner.rb TestFile.c my_config.yml extras.h
|
||||
```
|
||||
|
||||
Another option, particularly if you are already using Ruby to orchestrate your
|
||||
builds - or more likely the Ruby-based build tool Rake - is requiring this
|
||||
script directly. Anything that you would have specified in a YAML file can be
|
||||
passed to the script as part of a hash. Let's push the exact same requirement
|
||||
set as we did above but this time through Ruby code directly:
|
||||
|
||||
```Ruby
|
||||
require "generate_test_runner.rb"
|
||||
options = {
|
||||
:includes => ["stdio.h", "microdefs.h"],
|
||||
:cexception => 1,
|
||||
:suite_setup => "blah = malloc(1024);",
|
||||
:suite_teardown => "free(blah);"
|
||||
}
|
||||
UnityTestRunnerGenerator.new.run(testfile, runner_name, options)
|
||||
```
|
||||
|
||||
If you have multiple files to generate in a build script (such as a Rakefile),
|
||||
you might want to instantiate a generator object with your options and call it
|
||||
to generate each runner afterwards. Like thus:
|
||||
|
||||
```Ruby
|
||||
gen = UnityTestRunnerGenerator.new(options)
|
||||
test_files.each do |f|
|
||||
gen.run(f, File.basename(f,'.c')+"Runner.c"
|
||||
end
|
||||
```
|
||||
|
||||
#### Options accepted by generate_test_runner.rb:
|
||||
|
||||
The following options are available when executing `generate_test_runner`. You
|
||||
may pass these as a Ruby hash directly or specify them in a YAML file, both of
|
||||
which are described above. In the `examples` directory, Example 3's Rakefile
|
||||
demonstrates using a Ruby hash.
|
||||
|
||||
|
||||
##### `:includes`
|
||||
|
||||
This option specifies an array of file names to be `#include`'d at the top of
|
||||
your runner C file. You might use it to reference custom types or anything else
|
||||
universally needed in your generated runners.
|
||||
|
||||
|
||||
##### `:suite_setup`
|
||||
|
||||
Define this option with C code to be executed _before any_ test cases are run.
|
||||
|
||||
Alternatively, if your C compiler supports weak symbols, you can leave this
|
||||
option unset and instead provide a `void suiteSetUp(void)` function in your test
|
||||
suite. The linker will look for this symbol and fall back to a Unity-provided
|
||||
stub if it is not found.
|
||||
|
||||
|
||||
##### `:suite_teardown`
|
||||
|
||||
Define this option with C code to be executed _after all_ test cases have
|
||||
finished. An integer variable `num_failures` is available for diagnostics.
|
||||
The code should end with a `return` statement; the value returned will become
|
||||
the exit code of `main`. You can normally just return `num_failures`.
|
||||
|
||||
Alternatively, if your C compiler supports weak symbols, you can leave this
|
||||
option unset and instead provide a `int suiteTearDown(int num_failures)`
|
||||
function in your test suite. The linker will look for this symbol and fall
|
||||
back to a Unity-provided stub if it is not found.
|
||||
|
||||
|
||||
##### `:enforce_strict_ordering`
|
||||
|
||||
This option should be defined if you have the strict order feature enabled in
|
||||
CMock (see CMock documentation). This generates extra variables required for
|
||||
everything to run smoothly. If you provide the same YAML to the generator as
|
||||
used in CMock's configuration, you've already configured the generator properly.
|
||||
|
||||
##### `:mock_prefix` and `:mock_suffix`
|
||||
|
||||
Unity automatically generates calls to Init, Verify and Destroy for every file
|
||||
included in the main test file that starts with the given mock prefix and ends
|
||||
with the given mock suffix, file extension not included. By default, Unity
|
||||
assumes a `Mock` prefix and no suffix.
|
||||
|
||||
##### `:plugins`
|
||||
|
||||
This option specifies an array of plugins to be used (of course, the array can
|
||||
contain only a single plugin). This is your opportunity to enable support for
|
||||
CException support, which will add a check for unhandled exceptions in each
|
||||
test, reporting a failure if one is detected. To enable this feature using Ruby:
|
||||
|
||||
```Ruby
|
||||
:plugins => [ :cexception ]
|
||||
```
|
||||
|
||||
Or as a yaml file:
|
||||
|
||||
```YAML
|
||||
:plugins:
|
||||
-:cexception
|
||||
```
|
||||
|
||||
If you are using CMock, it is very likely that you are already passing an array
|
||||
of plugins to CMock. You can just use the same array here. This script will just
|
||||
ignore the plugins that don't require additional support.
|
||||
|
||||
|
||||
### `unity_test_summary.rb`
|
||||
|
||||
A Unity test file contains one or more test case functions. Each test case can
|
||||
pass, fail, or be ignored. Each test file is run individually producing results
|
||||
for its collection of test cases. A given project will almost certainly be
|
||||
composed of multiple test files. Therefore, the suite of tests is comprised of
|
||||
one or more test cases spread across one or more test files. This script
|
||||
aggregates individual test file results to generate a summary of all executed
|
||||
test cases. The output includes how many tests were run, how many were ignored,
|
||||
and how many failed. In addition, the output includes a listing of which
|
||||
specific tests were ignored and failed. A good example of the breadth and
|
||||
details of these results can be found in the `examples` directory. Intentionally
|
||||
ignored and failing tests in this project generate corresponding entries in the
|
||||
summary report.
|
||||
|
||||
If you're interested in other (prettier?) output formats, check into the
|
||||
Ceedling build tool project (ceedling.sourceforge.net) that works with Unity and
|
||||
CMock and supports xunit-style xml as well as other goodies.
|
||||
|
||||
This script assumes the existence of files ending with the extensions
|
||||
`.testpass` and `.testfail`.The contents of these files includes the test
|
||||
results summary corresponding to each test file executed with the extension set
|
||||
according to the presence or absence of failures for that test file. The script
|
||||
searches a specified path for these files, opens each one it finds, parses the
|
||||
results, and aggregates and prints a summary. Calling it from the command line
|
||||
looks like this:
|
||||
|
||||
```Shell
|
||||
ruby unity_test_summary.rb build/test/
|
||||
```
|
||||
|
||||
You can optionally specify a root path as well. This is really helpful when you
|
||||
are using relative paths in your tools' setup, but you want to pull the summary
|
||||
into an IDE like Eclipse for clickable shortcuts.
|
||||
|
||||
```Shell
|
||||
ruby unity_test_summary.rb build/test/ ~/projects/myproject/
|
||||
```
|
||||
|
||||
Or, if you're more of a Windows sort of person:
|
||||
|
||||
```Shell
|
||||
ruby unity_test_summary.rb build\teat\ C:\projects\myproject\
|
||||
```
|
||||
|
||||
When configured correctly, you'll see a final summary, like so:
|
||||
|
||||
```Shell
|
||||
--------------------------
|
||||
UNITY IGNORED TEST SUMMARY
|
||||
--------------------------
|
||||
blah.c:22:test_sandwiches_should_HaveBreadOnTwoSides:IGNORE
|
||||
|
||||
-------------------------
|
||||
UNITY FAILED TEST SUMMARY
|
||||
-------------------------
|
||||
blah.c:87:test_sandwiches_should_HaveCondiments:FAIL:Expected 1 was 0
|
||||
meh.c:38:test_soda_should_BeCalledPop:FAIL:Expected "pop" was "coke"
|
||||
|
||||
--------------------------
|
||||
OVERALL UNITY TEST SUMMARY
|
||||
--------------------------
|
||||
45 TOTAL TESTS 2 TOTAL FAILURES 1 IGNORED
|
||||
```
|
||||
|
||||
How convenient is that?
|
||||
|
||||
|
||||
*Find The Latest of This And More at [ThrowTheSwitch.org](https://throwtheswitch.org)*
|
99
test/vendor/ceedling/lib/ceedling.rb
vendored
Normal file
99
test/vendor/ceedling/lib/ceedling.rb
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
##
|
||||
# This module defines the interface for interacting with and loading a project
|
||||
# with Ceedling.
|
||||
module Ceedling
|
||||
##
|
||||
# Returns the location where the gem is installed.
|
||||
# === Return
|
||||
# _String_ - The location where the gem lives.
|
||||
def self.location
|
||||
File.join( File.dirname(__FILE__), '..')
|
||||
end
|
||||
|
||||
##
|
||||
# Return the path to the "built-in" plugins.
|
||||
# === Return
|
||||
# _String_ - The path where the default plugins live.
|
||||
def self.load_path
|
||||
File.join( self.location, 'plugins')
|
||||
end
|
||||
|
||||
##
|
||||
# Return the path to the Ceedling Rakefile
|
||||
# === Return
|
||||
# _String_
|
||||
def self.rakefile
|
||||
File.join( self.location, 'lib', 'ceedling', 'rakefile.rb' )
|
||||
end
|
||||
|
||||
##
|
||||
# This method selects the project file that Ceedling will use by setting the
|
||||
# CEEDLING_MAIN_PROJECT_FILE environment variable before loading the ceedling
|
||||
# rakefile. A path supplied as an argument to this method will override the
|
||||
# current value of the environment variable. If no path is supplied as an
|
||||
# argument then the existing value of the environment variable is used. If
|
||||
# the environment variable has not been set and no argument has been supplied
|
||||
# then a default path of './project.yml' will be used.
|
||||
#
|
||||
# === Arguments
|
||||
# +options+ _Hash_::
|
||||
# A hash containing the options for ceedling. Currently the following
|
||||
# options are supported:
|
||||
# * +config+ - The path to the project YAML configuration file.
|
||||
# * +root+ - The root of the project directory.
|
||||
# * +prefix+ - A prefix to prepend to plugin names in order to determine the
|
||||
# corresponding gem name.
|
||||
# * +plugins+ - The list of ceedling plugins to load
|
||||
def self.load_project(options = {})
|
||||
# Make sure our path to the yaml file is setup
|
||||
if options.has_key? :config
|
||||
ENV['CEEDLING_MAIN_PROJECT_FILE'] = options[:config]
|
||||
elsif ENV['CEEDLING_MAIN_PROJECT_FILE'].nil?
|
||||
ENV['CEEDLING_MAIN_PROJECT_FILE'] = './project.yml'
|
||||
end
|
||||
|
||||
# Register the plugins
|
||||
if options.has_key? :plugins
|
||||
options[:plugins].each do |plugin|
|
||||
register_plugin( plugin, options[:prefix] )
|
||||
end
|
||||
end
|
||||
|
||||
# Define the root of the project if specified
|
||||
Object.const_set('PROJECT_ROOT', options[:root]) if options.has_key? :root
|
||||
|
||||
# Load ceedling
|
||||
load "#{self.rakefile}"
|
||||
end
|
||||
|
||||
##
|
||||
# Register a plugin for ceedling to use when a project is loaded. This method
|
||||
# *must* be called prior to calling the _load_project_ method.
|
||||
#
|
||||
# This method is intended to be used for loading plugins distributed via the
|
||||
# RubyGems mechanism. As such, the following gem structure is assumed for
|
||||
# plugins.
|
||||
#
|
||||
# * The gem name must be prefixed with 'ceedling-' followed by the plugin
|
||||
# name (ex. 'ceedling-bullseye')
|
||||
#
|
||||
# * The contents of the plugin must be isntalled into a subdirectory of
|
||||
# the gem with the same name as the plugin (ex. 'bullseye/')
|
||||
#
|
||||
# === Arguments
|
||||
# +name+ _String_:: The name of the plugin to load.
|
||||
# +prefix+ _String_::
|
||||
# (optional, default = nil) The prefix to use for the full gem name.
|
||||
def self.register_plugin(name, prefix=nil)
|
||||
# Figure out the full name of the gem and location
|
||||
prefix ||= 'ceedling-'
|
||||
gem_name = prefix + name
|
||||
gem_dir = Gem::Specification.find_by_name(gem_name).gem_dir()
|
||||
|
||||
# Register the plugin with Ceedling
|
||||
require 'ceedling/defaults'
|
||||
DEFAULT_CEEDLING_CONFIG[:plugins][:enabled] << name
|
||||
DEFAULT_CEEDLING_CONFIG[:plugins][:load_paths] << gem_dir
|
||||
end
|
||||
end
|
||||
|
@ -1,10 +1,22 @@
|
||||
require 'constants'
|
||||
|
||||
require 'ceedling/constants'
|
||||
|
||||
##
|
||||
# Utilities for raiser and reporting errors during building.
|
||||
class BuildInvokerUtils
|
||||
|
||||
constructor :configurator, :streaminator
|
||||
|
||||
|
||||
##
|
||||
# Processes exceptions and tries to display a useful message for the user.
|
||||
#
|
||||
# ==== Attriboops...utes
|
||||
#
|
||||
# * _exception_: The exception given by a rescue statement.
|
||||
# * _context_: A symbol representing where in the build the exception
|
||||
# occurs.
|
||||
# * _test_build_: A bool to signify if the exception occurred while building
|
||||
# from test or source.
|
||||
#
|
||||
def process_exception(exception, context, test_build=true)
|
||||
if (exception.message =~ /Don't know how to build task '(.+)'/i)
|
||||
error_header = "ERROR: Rake could not find file referenced in source"
|
@ -1,6 +1,6 @@
|
||||
require 'defaults'
|
||||
require 'constants'
|
||||
require 'file_path_utils'
|
||||
require 'ceedling/defaults'
|
||||
require 'ceedling/constants'
|
||||
require 'ceedling/file_path_utils'
|
||||
require 'deep_merge'
|
||||
|
||||
|
||||
@ -9,14 +9,14 @@ class Configurator
|
||||
|
||||
attr_reader :project_config_hash, :script_plugins, :rake_plugins
|
||||
attr_accessor :project_logging, :project_debug, :project_verbosity, :sanity_checks
|
||||
|
||||
|
||||
constructor(:configurator_setup, :configurator_builder, :configurator_plugins, :cmock_builder, :yaml_wrapper, :system_wrapper) do
|
||||
@project_logging = false
|
||||
@project_debug = false
|
||||
@project_verbosity = Verbosity::NORMAL
|
||||
@sanity_checks = TestResultsSanityChecks::NORMAL
|
||||
end
|
||||
|
||||
|
||||
def setup
|
||||
# special copy of cmock config to provide to cmock for construction
|
||||
@cmock_config_hash = {}
|
||||
@ -25,23 +25,23 @@ class Configurator
|
||||
# in eval() statements in build() have something of proper scope and persistence to reference
|
||||
@project_config_hash = {}
|
||||
@project_config_hash_backup = {}
|
||||
|
||||
|
||||
@script_plugins = []
|
||||
@rake_plugins = []
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def replace_flattened_config(config)
|
||||
@project_config_hash.merge!(config)
|
||||
@configurator_setup.build_constants_and_accessors(@project_config_hash, binding())
|
||||
end
|
||||
|
||||
|
||||
|
||||
def store_config
|
||||
@project_config_hash_backup = @project_config_hash.clone
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def restore_config
|
||||
@project_config_hash = @project_config_hash_backup
|
||||
@configurator_setup.build_constants_and_accessors(@project_config_hash, binding())
|
||||
@ -72,13 +72,18 @@ class Configurator
|
||||
@configurator_builder.populate_defaults( config, DEFAULT_TOOLS_TEST )
|
||||
@configurator_builder.populate_defaults( config, DEFAULT_TOOLS_TEST_PREPROCESSORS ) if (config[:project][:use_test_preprocessor])
|
||||
@configurator_builder.populate_defaults( config, DEFAULT_TOOLS_TEST_DEPENDENCIES ) if (config[:project][:use_deep_dependencies])
|
||||
|
||||
|
||||
@configurator_builder.populate_defaults( config, DEFAULT_TOOLS_RELEASE ) if (config[:project][:release_build])
|
||||
@configurator_builder.populate_defaults( config, DEFAULT_TOOLS_RELEASE_ASSEMBLER ) if (config[:project][:release_build] and config[:release_build][:use_assembly])
|
||||
@configurator_builder.populate_defaults( config, DEFAULT_TOOLS_RELEASE_DEPENDENCIES ) if (config[:project][:release_build] and config[:project][:use_deep_dependencies])
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def populate_unity_defaults(config)
|
||||
unity = config[:unity] || {}
|
||||
@runner_config = unity.merge(@runner_config || config[:test_runner] || {})
|
||||
end
|
||||
|
||||
def populate_cmock_defaults(config)
|
||||
# cmock has its own internal defaults handling, but we need to set these specific values
|
||||
# so they're present for the build environment to access;
|
||||
@ -87,10 +92,10 @@ class Configurator
|
||||
|
||||
# yes, we're duplicating the default mock_prefix in cmock, but it's because we need CMOCK_MOCK_PREFIX always available in Ceedling's environment
|
||||
cmock[:mock_prefix] = 'Mock' if (cmock[:mock_prefix].nil?)
|
||||
|
||||
|
||||
# just because strict ordering is the way to go
|
||||
cmock[:enforce_strict_ordering] = true if (cmock[:enforce_strict_ordering].nil?)
|
||||
|
||||
|
||||
cmock[:mock_path] = File.join(config[:project][:build_root], TESTS_BASE_PATH, 'mocks') if (cmock[:mock_path].nil?)
|
||||
cmock[:verbosity] = @project_verbosity if (cmock[:verbosity].nil?)
|
||||
|
||||
@ -100,17 +105,19 @@ class Configurator
|
||||
cmock[:plugins].uniq!
|
||||
|
||||
cmock[:unity_helper] = false if (cmock[:unity_helper].nil?)
|
||||
|
||||
|
||||
if (cmock[:unity_helper])
|
||||
cmock[:includes] << File.basename(cmock[:unity_helper])
|
||||
cmock[:unity_helper] = [cmock[:unity_helper]] if cmock[:unity_helper].is_a? String
|
||||
cmock[:includes] += cmock[:unity_helper].map{|helper| File.basename(helper) }
|
||||
cmock[:includes].uniq!
|
||||
end
|
||||
|
||||
@runner_config = cmock.merge(config[:test_runner] || {})
|
||||
@runner_config = cmock.merge(@runner_config || config[:test_runner] || {})
|
||||
|
||||
@cmock_builder.manufacture(cmock)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def get_runner_config
|
||||
@runner_config
|
||||
end
|
||||
@ -122,9 +129,14 @@ class Configurator
|
||||
config[:tools].each_key do |name|
|
||||
tool = config[:tools][name]
|
||||
|
||||
# populate name if not given
|
||||
# populate name if not given
|
||||
tool[:name] = name.to_s if (tool[:name].nil?)
|
||||
|
||||
# handle inline ruby string substitution in executable
|
||||
if (tool[:executable] =~ RUBY_STRING_REPLACEMENT_PATTERN)
|
||||
tool[:executable].replace(@system_wrapper.module_eval(tool[:executable]))
|
||||
end
|
||||
|
||||
# populate stderr redirect option
|
||||
tool[:stderr_redirect] = StdErrRedirect::NONE if (tool[:stderr_redirect].nil?)
|
||||
|
||||
@ -135,20 +147,19 @@ class Configurator
|
||||
tool[:optional] = false if (tool[:optional].nil?)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def tools_supplement_arguments(config)
|
||||
tools_name_prefix = 'tools_'
|
||||
config[:tools].each_key do |name|
|
||||
tool = @project_config_hash[(tools_name_prefix + name.to_s).to_sym]
|
||||
|
||||
# smoosh in extra arguments if specified at top-level of config (useful for plugins & default gcc tools)
|
||||
# arguments are squirted in at beginning of list
|
||||
# arguments are squirted in at _end_ of list
|
||||
top_level_tool = (tools_name_prefix + name.to_s).to_sym
|
||||
if (not config[top_level_tool].nil?)
|
||||
# adding and flattening is not a good idea: might over-flatten if there's array nesting in tool args
|
||||
# use _with_index to preserve order
|
||||
config[top_level_tool][:arguments].each_with_index { |arg, index| tool[:arguments].insert( index, arg ) }
|
||||
tool[:arguments].concat config[top_level_tool][:arguments]
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -161,49 +172,64 @@ class Configurator
|
||||
path.replace(@system_wrapper.module_eval(path)) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN)
|
||||
FilePathUtils::standardize(path)
|
||||
end
|
||||
|
||||
|
||||
config[:plugins][:load_paths] << FilePathUtils::standardize(Ceedling.load_path)
|
||||
config[:plugins][:load_paths].uniq!
|
||||
|
||||
paths_hash = @configurator_plugins.add_load_paths(config)
|
||||
|
||||
@rake_plugins = @configurator_plugins.find_rake_plugins(config)
|
||||
@script_plugins = @configurator_plugins.find_script_plugins(config)
|
||||
config_plugins = @configurator_plugins.find_config_plugins(config)
|
||||
plugin_defaults = @configurator_plugins.find_plugin_defaults(config)
|
||||
|
||||
|
||||
@rake_plugins = @configurator_plugins.find_rake_plugins(config, paths_hash)
|
||||
@script_plugins = @configurator_plugins.find_script_plugins(config, paths_hash)
|
||||
config_plugins = @configurator_plugins.find_config_plugins(config, paths_hash)
|
||||
plugin_defaults = @configurator_plugins.find_plugin_defaults(config, paths_hash)
|
||||
|
||||
config_plugins.each do |plugin|
|
||||
config.deep_merge( @yaml_wrapper.load(plugin) )
|
||||
config.deep_merge!( @yaml_wrapper.load(plugin) )
|
||||
end
|
||||
|
||||
|
||||
plugin_defaults.each do |defaults|
|
||||
@configurator_builder.populate_defaults( config, @yaml_wrapper.load(defaults) )
|
||||
end
|
||||
|
||||
|
||||
# special plugin setting for results printing
|
||||
config[:plugins][:display_raw_test_results] = true if (config[:plugins][:display_raw_test_results].nil?)
|
||||
|
||||
|
||||
paths_hash.each_pair { |name, path| config[:plugins][name] = path }
|
||||
end
|
||||
|
||||
|
||||
|
||||
def merge_imports(config)
|
||||
if config[:import]
|
||||
until config[:import].empty?
|
||||
path = config[:import].shift
|
||||
path = @system_wrapper.module_eval(path) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN)
|
||||
config.deep_merge!(@yaml_wrapper.load(path))
|
||||
end
|
||||
end
|
||||
config.delete(:import)
|
||||
end
|
||||
|
||||
|
||||
def eval_environment_variables(config)
|
||||
config[:environment].each do |hash|
|
||||
key = hash.keys[0]
|
||||
value = hash[key]
|
||||
items = []
|
||||
|
||||
|
||||
interstitial = ((key == :path) ? File::PATH_SEPARATOR : '')
|
||||
items = ((value.class == Array) ? hash[key] : [value])
|
||||
|
||||
|
||||
items.each { |item| item.replace( @system_wrapper.module_eval( item ) ) if (item =~ RUBY_STRING_REPLACEMENT_PATTERN) }
|
||||
hash[key] = items.join( interstitial )
|
||||
|
||||
|
||||
@system_wrapper.env_set( key.to_s.upcase, hash[key] )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def eval_paths(config)
|
||||
# [:plugins]:[load_paths] already handled
|
||||
|
||||
|
||||
paths = [ # individual paths that don't follow convention processed below
|
||||
config[:project][:build_root],
|
||||
config[:release_build][:artifacts]]
|
||||
@ -212,17 +238,17 @@ class Configurator
|
||||
|
||||
config[:paths].each_pair { |collection, paths| eval_path_list( paths ) }
|
||||
|
||||
config[:files].each_pair { |collection, files| eval_path_list( paths ) }
|
||||
|
||||
config[:files].each_pair { |collection, files| eval_path_list( files ) }
|
||||
|
||||
# all other paths at secondary hash key level processed by convention:
|
||||
# ex. [:toplevel][:foo_path] & [:toplevel][:bar_paths] are evaluated
|
||||
config.each_pair { |parent, child| eval_path_list( collect_path_list( child ) ) }
|
||||
config.each_pair { |parent, child| eval_path_list( collect_path_list( child ) ) }
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def standardize_paths(config)
|
||||
# [:plugins]:[load_paths] already handled
|
||||
|
||||
|
||||
paths = [ # individual paths that don't follow convention processed below
|
||||
config[:project][:build_root],
|
||||
config[:release_build][:artifacts]] # cmock path in case it was explicitly set in config
|
||||
@ -230,52 +256,52 @@ class Configurator
|
||||
paths.flatten.each { |path| FilePathUtils::standardize( path ) }
|
||||
|
||||
config[:paths].each_pair do |collection, paths|
|
||||
paths.each{|path| FilePathUtils::standardize( path )}
|
||||
# ensure that list is an array (i.e. handle case of list being a single string)
|
||||
config[:paths][collection] = [paths].flatten
|
||||
# ensure that list is an array (i.e. handle case of list being a single string,
|
||||
# or a multidimensional array)
|
||||
config[:paths][collection] = [paths].flatten.map{|path| FilePathUtils::standardize( path )}
|
||||
end
|
||||
|
||||
config[:files].each_pair { |collection, files| files.each{ |path| FilePathUtils::standardize( path ) } }
|
||||
|
||||
config[:tools].each_pair { |tool, config| FilePathUtils::standardize( config[:executable] ) }
|
||||
|
||||
config[:tools].each_pair { |tool, config| FilePathUtils::standardize( config[:executable] ) if (config.include? :executable) }
|
||||
|
||||
# all other paths at secondary hash key level processed by convention:
|
||||
# ex. [:toplevel][:foo_path] & [:toplevel][:bar_paths] are standardized
|
||||
config.each_pair do |parent, child|
|
||||
collect_path_list( child ).each { |path| FilePathUtils::standardize( path ) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def validate(config)
|
||||
# collect felonies and go straight to jail
|
||||
raise if (not @configurator_setup.validate_required_sections( config ))
|
||||
|
||||
|
||||
# collect all misdemeanors, everybody on probation
|
||||
blotter = []
|
||||
blotter << @configurator_setup.validate_required_section_values( config )
|
||||
blotter << @configurator_setup.validate_paths( config )
|
||||
blotter << @configurator_setup.validate_tools( config )
|
||||
blotter << @configurator_setup.validate_plugins( config )
|
||||
|
||||
|
||||
raise if (blotter.include?( false ))
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
# create constants and accessors (attached to this object) from given hash
|
||||
def build(config, *keys)
|
||||
# create flattened & expanded configuration hash
|
||||
built_config = @configurator_setup.build_project_config( config, @configurator_builder.flattenify( config ) )
|
||||
|
||||
|
||||
@project_config_hash = built_config.clone
|
||||
store_config()
|
||||
|
||||
@configurator_setup.build_constants_and_accessors(built_config, binding())
|
||||
|
||||
|
||||
# top-level keys disappear when we flatten, so create global constants & accessors to any specified keys
|
||||
keys.each do |key|
|
||||
hash = { key => config[key] }
|
||||
@configurator_setup.build_constants_and_accessors(hash, binding())
|
||||
@configurator_setup.build_constants_and_accessors(hash, binding())
|
||||
end
|
||||
end
|
||||
|
||||
@ -287,30 +313,30 @@ class Configurator
|
||||
|
||||
# flatten our addition hash
|
||||
config_more_flattened = @configurator_builder.flattenify( config_more )
|
||||
|
||||
|
||||
# merge our flattened hash with built hash from previous build
|
||||
@project_config_hash.deep_merge!( config_more_flattened )
|
||||
store_config()
|
||||
|
||||
# create more constants and accessors
|
||||
@configurator_setup.build_constants_and_accessors(config_more_flattened, binding())
|
||||
|
||||
|
||||
# recreate constants & update accessors with new merged, base values
|
||||
config_more.keys.each do |key|
|
||||
hash = { key => config_base[key] }
|
||||
@configurator_setup.build_constants_and_accessors(hash, binding())
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def insert_rake_plugins(plugins)
|
||||
plugins.each do |plugin|
|
||||
@project_config_hash[:project_rakefile_component_files] << plugin
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
### private ###
|
||||
|
||||
|
||||
private
|
||||
|
||||
def collect_path_list( container )
|
||||
@ -318,12 +344,13 @@ class Configurator
|
||||
container.each_key { |key| paths << container[key] if (key.to_s =~ /_path(s)?$/) } if (container.class == Hash)
|
||||
return paths.flatten
|
||||
end
|
||||
|
||||
|
||||
def eval_path_list( paths )
|
||||
paths.flatten.each do |path|
|
||||
path.replace( @system_wrapper.module_eval( path ) ) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
@ -1,16 +1,16 @@
|
||||
require 'rubygems'
|
||||
require 'rake' # for ext() method
|
||||
require 'file_path_utils' # for class methods
|
||||
require 'defaults'
|
||||
require 'constants' # for Verbosity constants class & base file paths
|
||||
require 'ceedling/file_path_utils' # for class methods
|
||||
require 'ceedling/defaults'
|
||||
require 'ceedling/constants' # for Verbosity constants class & base file paths
|
||||
|
||||
|
||||
|
||||
class ConfiguratorBuilder
|
||||
|
||||
|
||||
constructor :file_system_utils, :file_wrapper, :system_wrapper
|
||||
|
||||
|
||||
|
||||
|
||||
def build_global_constants(config)
|
||||
config.each_pair do |key, value|
|
||||
formatted_key = key.to_s.upcase
|
||||
@ -21,7 +21,7 @@ class ConfiguratorBuilder
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def build_accessor_methods(config, context)
|
||||
config.each_pair do |key, value|
|
||||
# fill configurator object with accessor methods
|
||||
@ -29,11 +29,11 @@ class ConfiguratorBuilder
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# create a flattened hash from the original configuration structure
|
||||
def flattenify(config)
|
||||
new_hash = {}
|
||||
|
||||
|
||||
config.each_key do | parent |
|
||||
|
||||
# gracefully handle empty top-level entries
|
||||
@ -54,17 +54,18 @@ class ConfiguratorBuilder
|
||||
else
|
||||
new_hash["#{parent.to_s.downcase}".to_sym] = config[parent]
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return new_hash
|
||||
end
|
||||
|
||||
|
||||
|
||||
def populate_defaults(config, defaults)
|
||||
defaults.keys.sort.each do |section|
|
||||
defaults[section].keys.sort.each do |entry|
|
||||
config[section][entry] = defaults[section][entry].deep_clone if (config[section].nil? or config[section][entry].nil?)
|
||||
config[section] = {} if config[section].nil?
|
||||
config[section][entry] = defaults[section][entry].deep_clone if (config[section][entry].nil?)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -88,12 +89,14 @@ class ConfiguratorBuilder
|
||||
[:project_build_tests_root, project_build_tests_root, true ],
|
||||
[:project_build_release_root, project_build_release_root, in_hash[:project_release_build] ],
|
||||
|
||||
[:project_test_artifacts_path, File.join(project_build_artifacts_root, TESTS_BASE_PATH), true ],
|
||||
[:project_test_runners_path, File.join(project_build_tests_root, 'runners'), true ],
|
||||
[:project_test_results_path, File.join(project_build_tests_root, 'results'), true ],
|
||||
[:project_test_build_output_path, File.join(project_build_tests_root, 'out'), true ],
|
||||
[:project_test_build_cache_path, File.join(project_build_tests_root, 'cache'), true ],
|
||||
[:project_test_dependencies_path, File.join(project_build_tests_root, 'dependencies'), true ],
|
||||
[:project_test_artifacts_path, File.join(project_build_artifacts_root, TESTS_BASE_PATH), true ],
|
||||
[:project_test_runners_path, File.join(project_build_tests_root, 'runners'), true ],
|
||||
[:project_test_results_path, File.join(project_build_tests_root, 'results'), true ],
|
||||
[:project_test_build_output_path, File.join(project_build_tests_root, 'out'), true ],
|
||||
[:project_test_build_output_asm_path, File.join(project_build_tests_root, 'out', 'asm'), true ],
|
||||
[:project_test_build_output_c_path, File.join(project_build_tests_root, 'out', 'c'), true ],
|
||||
[:project_test_build_cache_path, File.join(project_build_tests_root, 'cache'), true ],
|
||||
[:project_test_dependencies_path, File.join(project_build_tests_root, 'dependencies'), true ],
|
||||
|
||||
[:project_release_artifacts_path, File.join(project_build_artifacts_root, RELEASE_BASE_PATH), in_hash[:project_release_build] ],
|
||||
[:project_release_build_cache_path, File.join(project_build_release_root, 'cache'), in_hash[:project_release_build] ],
|
||||
@ -118,7 +121,7 @@ class ConfiguratorBuilder
|
||||
build_path_name = path[0]
|
||||
build_path = path[1]
|
||||
build_path_add_condition = path[2]
|
||||
|
||||
|
||||
# insert path into build paths if associated with true condition
|
||||
out_hash[:project_build_paths] << build_path if build_path_add_condition
|
||||
# set path symbol name and path for each entry in paths array
|
||||
@ -131,7 +134,7 @@ class ConfiguratorBuilder
|
||||
|
||||
def set_force_build_filepaths(in_hash)
|
||||
out_hash = {}
|
||||
|
||||
|
||||
out_hash[:project_test_force_rebuild_filepath] = File.join( in_hash[:project_test_dependencies_path], 'force_build' )
|
||||
out_hash[:project_release_force_rebuild_filepath] = File.join( in_hash[:project_release_dependencies_path], 'force_build' ) if (in_hash[:project_release_build])
|
||||
|
||||
@ -141,138 +144,118 @@ class ConfiguratorBuilder
|
||||
|
||||
def set_rakefile_components(in_hash)
|
||||
out_hash = {
|
||||
:project_rakefile_component_files =>
|
||||
[File.join(CEEDLING_LIB, 'tasks_base.rake'),
|
||||
File.join(CEEDLING_LIB, 'tasks_filesystem.rake'),
|
||||
File.join(CEEDLING_LIB, 'tasks_tests.rake'),
|
||||
File.join(CEEDLING_LIB, 'tasks_vendor.rake'),
|
||||
File.join(CEEDLING_LIB, 'rules_tests.rake')]}
|
||||
:project_rakefile_component_files =>
|
||||
[File.join(CEEDLING_LIB, 'ceedling', 'tasks_base.rake'),
|
||||
File.join(CEEDLING_LIB, 'ceedling', 'tasks_filesystem.rake'),
|
||||
File.join(CEEDLING_LIB, 'ceedling', 'tasks_tests.rake'),
|
||||
File.join(CEEDLING_LIB, 'ceedling', 'tasks_vendor.rake'),
|
||||
File.join(CEEDLING_LIB, 'ceedling', 'rules_tests.rake')]}
|
||||
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'rules_cmock.rake') if (in_hash[:project_use_mocks])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'rules_preprocess.rake') if (in_hash[:project_use_test_preprocessor])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'rules_tests_deep_dependencies.rake') if (in_hash[:project_use_deep_dependencies])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'tasks_tests_deep_dependencies.rake') if (in_hash[:project_use_deep_dependencies])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'rules_cmock.rake') if (in_hash[:project_use_mocks])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'rules_preprocess.rake') if (in_hash[:project_use_test_preprocessor])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'rules_tests_deep_dependencies.rake') if (in_hash[:project_use_deep_dependencies])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'tasks_tests_deep_dependencies.rake') if (in_hash[:project_use_deep_dependencies])
|
||||
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'rules_release_deep_dependencies.rake') if (in_hash[:project_release_build] and in_hash[:project_use_deep_dependencies])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'rules_release.rake') if (in_hash[:project_release_build])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'tasks_release_deep_dependencies.rake') if (in_hash[:project_release_build] and in_hash[:project_use_deep_dependencies])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'tasks_release.rake') if (in_hash[:project_release_build])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'rules_release_deep_dependencies.rake') if (in_hash[:project_release_build] and in_hash[:project_use_deep_dependencies])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'rules_release.rake') if (in_hash[:project_release_build])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'tasks_release_deep_dependencies.rake') if (in_hash[:project_release_build] and in_hash[:project_use_deep_dependencies])
|
||||
out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'ceedling', 'tasks_release.rake') if (in_hash[:project_release_build])
|
||||
|
||||
return out_hash
|
||||
end
|
||||
|
||||
|
||||
def set_library_build_info_filepaths(hash)
|
||||
|
||||
# Notes:
|
||||
# - Dependency on a change to our input configuration hash is handled elsewhere as it is
|
||||
# dynamically formed during ceedling's execution
|
||||
# - Compiled vendor dependencies like cmock.o, unity.o, cexception.o are handled below;
|
||||
# here we're interested only in ceedling-based code generation dependencies
|
||||
|
||||
ceedling_build_info_filepath = File.join(CEEDLING_RELEASE, 'build.info')
|
||||
cmock_build_info_filepath = FilePathUtils::form_ceedling_vendor_path('cmock/release', 'build.info')
|
||||
|
||||
out_hash = {
|
||||
:ceedling_build_info_filepath => ceedling_build_info_filepath,
|
||||
:cmock_build_info_filepath => cmock_build_info_filepath
|
||||
}
|
||||
|
||||
return out_hash
|
||||
end
|
||||
|
||||
|
||||
def set_release_target(in_hash)
|
||||
return {} if (not in_hash[:project_release_build])
|
||||
|
||||
|
||||
release_target_file = ((in_hash[:release_build_output].nil?) ? (DEFAULT_RELEASE_TARGET_NAME.ext(in_hash[:extension_executable])) : in_hash[:release_build_output])
|
||||
release_map_file = ((in_hash[:release_build_output].nil?) ? (DEFAULT_RELEASE_TARGET_NAME.ext(in_hash[:extension_map])) : in_hash[:release_build_output].ext(in_hash[:extension_map]))
|
||||
|
||||
|
||||
return {
|
||||
# tempted to make a helper method in file_path_utils? stop right there, pal. you'll introduce a cyclical dependency
|
||||
:project_release_build_target => File.join(in_hash[:project_build_release_root], release_target_file),
|
||||
:project_release_build_map => File.join(in_hash[:project_build_release_root], release_map_file)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
|
||||
def collect_project_options(in_hash)
|
||||
options = []
|
||||
|
||||
|
||||
in_hash[:project_options_paths].each do |path|
|
||||
options << @file_wrapper.directory_listing( File.join(path, '*.yml') )
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
:collection_project_options => options.flatten
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
|
||||
def expand_all_path_globs(in_hash)
|
||||
out_hash = {}
|
||||
path_keys = []
|
||||
|
||||
|
||||
in_hash.each_key do |key|
|
||||
next if (not key.to_s[0..4] == 'paths')
|
||||
path_keys << key
|
||||
end
|
||||
|
||||
|
||||
# sorted to provide assured order of traversal in test calls on mocks
|
||||
path_keys.sort.each do |key|
|
||||
out_hash["collection_#{key.to_s}".to_sym] = @file_system_utils.collect_paths( in_hash[key] )
|
||||
end
|
||||
|
||||
|
||||
return out_hash
|
||||
end
|
||||
|
||||
|
||||
def collect_source_and_include_paths(in_hash)
|
||||
return {
|
||||
:collection_paths_source_and_include =>
|
||||
( in_hash[:collection_paths_source] +
|
||||
:collection_paths_source_and_include =>
|
||||
( in_hash[:collection_paths_source] +
|
||||
in_hash[:collection_paths_include] ).select {|x| File.directory?(x)}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
def collect_source_include_vendor_paths(in_hash)
|
||||
extra_paths = []
|
||||
extra_paths << FilePathUtils::form_ceedling_vendor_path(CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions])
|
||||
extra_paths << File.join(in_hash[:cexception_vendor_path], CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions])
|
||||
|
||||
return {
|
||||
:collection_paths_source_include_vendor =>
|
||||
in_hash[:collection_paths_source_and_include] +
|
||||
:collection_paths_source_include_vendor =>
|
||||
in_hash[:collection_paths_source_and_include] +
|
||||
extra_paths
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
def collect_test_support_source_include_paths(in_hash)
|
||||
return {
|
||||
:collection_paths_test_support_source_include =>
|
||||
:collection_paths_test_support_source_include =>
|
||||
(in_hash[:collection_paths_test] +
|
||||
in_hash[:collection_paths_support] +
|
||||
in_hash[:collection_paths_source] +
|
||||
in_hash[:collection_paths_source] +
|
||||
in_hash[:collection_paths_include] ).select {|x| File.directory?(x)}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
def collect_vendor_paths(in_hash)
|
||||
return {:collection_paths_vendor => get_vendor_paths(in_hash)}
|
||||
end
|
||||
|
||||
|
||||
|
||||
def collect_test_support_source_include_vendor_paths(in_hash)
|
||||
return {
|
||||
:collection_paths_test_support_source_include_vendor =>
|
||||
:collection_paths_test_support_source_include_vendor =>
|
||||
in_hash[:collection_paths_test_support_source_include] +
|
||||
get_vendor_paths(in_hash)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def collect_tests(in_hash)
|
||||
all_tests = @file_wrapper.instantiate_file_list
|
||||
|
||||
@ -289,12 +272,12 @@ class ConfiguratorBuilder
|
||||
def collect_assembly(in_hash)
|
||||
all_assembly = @file_wrapper.instantiate_file_list
|
||||
|
||||
return {:collection_all_assembly => all_assembly} if (not in_hash[:release_build_use_assembly])
|
||||
|
||||
return {:collection_all_assembly => all_assembly} if ((not in_hash[:release_build_use_assembly]) && (not in_hash[:test_build_use_assembly]))
|
||||
|
||||
in_hash[:collection_paths_source].each do |path|
|
||||
all_assembly.include( File.join(path, "*#{in_hash[:extension_assembly]}") )
|
||||
end
|
||||
|
||||
|
||||
@file_system_utils.revise_file_list( all_assembly, in_hash[:files_assembly] )
|
||||
|
||||
return {:collection_all_assembly => all_assembly}
|
||||
@ -311,7 +294,7 @@ class ConfiguratorBuilder
|
||||
end
|
||||
end
|
||||
@file_system_utils.revise_file_list( all_source, in_hash[:files_source] )
|
||||
|
||||
|
||||
return {:collection_all_source => all_source}
|
||||
end
|
||||
|
||||
@ -319,34 +302,60 @@ class ConfiguratorBuilder
|
||||
def collect_headers(in_hash)
|
||||
all_headers = @file_wrapper.instantiate_file_list
|
||||
|
||||
paths =
|
||||
paths =
|
||||
in_hash[:collection_paths_test] +
|
||||
in_hash[:collection_paths_support] +
|
||||
in_hash[:collection_paths_source] +
|
||||
in_hash[:collection_paths_source] +
|
||||
in_hash[:collection_paths_include]
|
||||
|
||||
|
||||
paths.each do |path|
|
||||
all_headers.include( File.join(path, "*#{in_hash[:extension_header]}") )
|
||||
end
|
||||
|
||||
@file_system_utils.revise_file_list( all_headers, in_hash[:files_include] )
|
||||
|
||||
|
||||
return {:collection_all_headers => all_headers}
|
||||
end
|
||||
|
||||
|
||||
def collect_release_existing_compilation_input(in_hash)
|
||||
release_input = @file_wrapper.instantiate_file_list
|
||||
|
||||
paths =
|
||||
in_hash[:collection_paths_source] +
|
||||
in_hash[:collection_paths_include]
|
||||
|
||||
paths << File.join(in_hash[:cexception_vendor_path], CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions])
|
||||
|
||||
paths.each do |path|
|
||||
release_input.include( File.join(path, "*#{in_hash[:extension_header]}") )
|
||||
if File.exists?(path) and not File.directory?(path)
|
||||
release_input.include( path )
|
||||
else
|
||||
release_input.include( File.join(path, "*#{in_hash[:extension_source]}") )
|
||||
end
|
||||
end
|
||||
|
||||
@file_system_utils.revise_file_list( release_input, in_hash[:files_source] )
|
||||
@file_system_utils.revise_file_list( release_input, in_hash[:files_include] )
|
||||
# finding assembly files handled explicitly through other means
|
||||
|
||||
return {:collection_release_existing_compilation_input => release_input}
|
||||
end
|
||||
|
||||
|
||||
def collect_all_existing_compilation_input(in_hash)
|
||||
all_input = @file_wrapper.instantiate_file_list
|
||||
|
||||
paths =
|
||||
in_hash[:collection_paths_test] +
|
||||
in_hash[:collection_paths_support] +
|
||||
in_hash[:collection_paths_source] +
|
||||
paths =
|
||||
in_hash[:collection_paths_test] +
|
||||
in_hash[:collection_paths_support] +
|
||||
in_hash[:collection_paths_source] +
|
||||
in_hash[:collection_paths_include] +
|
||||
[FilePathUtils::form_ceedling_vendor_path(UNITY_LIB_PATH)]
|
||||
|
||||
paths << FilePathUtils::form_ceedling_vendor_path(CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions])
|
||||
paths << FilePathUtils::form_ceedling_vendor_path(CMOCK_LIB_PATH) if (in_hash[:project_use_mocks])
|
||||
[File.join(in_hash[:unity_vendor_path], UNITY_LIB_PATH)]
|
||||
|
||||
paths << File.join(in_hash[:cexception_vendor_path], CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions])
|
||||
paths << File.join(in_hash[:cmock_vendor_path], CMOCK_LIB_PATH) if (in_hash[:project_use_mocks])
|
||||
|
||||
paths.each do |path|
|
||||
all_input.include( File.join(path, "*#{in_hash[:extension_header]}") )
|
||||
@ -354,16 +363,17 @@ class ConfiguratorBuilder
|
||||
all_input.include( path )
|
||||
else
|
||||
all_input.include( File.join(path, "*#{in_hash[:extension_source]}") )
|
||||
all_input.include( File.join(path, "*#{in_hash[:extension_assembly]}") ) if (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@file_system_utils.revise_file_list( all_input, in_hash[:files_test] )
|
||||
@file_system_utils.revise_file_list( all_input, in_hash[:files_support] )
|
||||
@file_system_utils.revise_file_list( all_input, in_hash[:files_source] )
|
||||
@file_system_utils.revise_file_list( all_input, in_hash[:files_include] )
|
||||
# finding assembly files handled explicitly through other means
|
||||
|
||||
return {:collection_all_existing_compilation_input => all_input}
|
||||
return {:collection_all_existing_compilation_input => all_input}
|
||||
end
|
||||
|
||||
|
||||
@ -373,16 +383,16 @@ class ConfiguratorBuilder
|
||||
test_defines.concat(in_hash[:unity_defines])
|
||||
test_defines.concat(in_hash[:cmock_defines]) if (in_hash[:project_use_mocks])
|
||||
test_defines.concat(in_hash[:cexception_defines]) if (in_hash[:project_use_exceptions])
|
||||
|
||||
|
||||
return {:collection_defines_test_and_vendor => test_defines}
|
||||
end
|
||||
|
||||
|
||||
def collect_release_and_vendor_defines(in_hash)
|
||||
release_defines = in_hash[:defines_release].clone
|
||||
|
||||
|
||||
release_defines.concat(in_hash[:cexception_defines]) if (in_hash[:project_use_exceptions])
|
||||
|
||||
|
||||
return {:collection_defines_release_and_vendor => release_defines}
|
||||
end
|
||||
|
||||
@ -395,29 +405,33 @@ class ConfiguratorBuilder
|
||||
|
||||
return {:collection_release_artifact_extra_link_objects => objects}
|
||||
end
|
||||
|
||||
|
||||
|
||||
def collect_test_fixture_extra_link_objects(in_hash)
|
||||
# Note: Symbols passed to compiler at command line can change Unity and CException behavior / configuration;
|
||||
# we also handle those dependencies elsewhere in compilation dependencies
|
||||
|
||||
|
||||
objects = [UNITY_C_FILE]
|
||||
|
||||
|
||||
in_hash[:files_support].each { |file| objects << File.basename(file) }
|
||||
|
||||
# we don't include paths here because use of plugins or mixing different compilers may require different build paths
|
||||
objects << CEXCEPTION_C_FILE if (in_hash[:project_use_exceptions])
|
||||
objects << CMOCK_C_FILE if (in_hash[:project_use_mocks])
|
||||
|
||||
|
||||
# if we're using mocks & a unity helper is defined & that unity helper includes a source file component (not only a header of macros),
|
||||
# then link in the unity_helper object file too
|
||||
if ( in_hash[:project_use_mocks] and
|
||||
in_hash[:cmock_unity_helper] and
|
||||
@file_wrapper.exist?(in_hash[:cmock_unity_helper].ext(in_hash[:extension_source])) )
|
||||
objects << File.basename(in_hash[:cmock_unity_helper])
|
||||
if ( in_hash[:project_use_mocks] and in_hash[:cmock_unity_helper] )
|
||||
in_hash[:cmock_unity_helper].each do |helper|
|
||||
if @file_wrapper.exist?(helper.ext(in_hash[:extension_source]))
|
||||
objects << File.basename(helper)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# no build paths here so plugins can remap if necessary (i.e. path mapping happens at runtime)
|
||||
objects.map! { |object| object.ext(in_hash[:extension_object]) }
|
||||
|
||||
|
||||
return { :collection_test_fixture_extra_link_objects => objects }
|
||||
end
|
||||
|
||||
@ -426,12 +440,12 @@ class ConfiguratorBuilder
|
||||
|
||||
def get_vendor_paths(in_hash)
|
||||
vendor_paths = []
|
||||
vendor_paths << FilePathUtils::form_ceedling_vendor_path(UNITY_LIB_PATH)
|
||||
vendor_paths << FilePathUtils::form_ceedling_vendor_path(CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions])
|
||||
vendor_paths << FilePathUtils::form_ceedling_vendor_path(CMOCK_LIB_PATH) if (in_hash[:project_use_mocks])
|
||||
vendor_paths << in_hash[:cmock_mock_path] if (in_hash[:project_use_mocks])
|
||||
vendor_paths << File.join(in_hash[:unity_vendor_path], UNITY_LIB_PATH)
|
||||
vendor_paths << File.join(in_hash[:cexception_vendor_path], CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions])
|
||||
vendor_paths << File.join(in_hash[:cmock_vendor_path], CMOCK_LIB_PATH) if (in_hash[:project_use_mocks])
|
||||
vendor_paths << in_hash[:cmock_mock_path] if (in_hash[:project_use_mocks])
|
||||
|
||||
return vendor_paths
|
||||
end
|
||||
|
||||
|
||||
end
|
111
test/vendor/ceedling/lib/ceedling/configurator_plugins.rb
vendored
Normal file
111
test/vendor/ceedling/lib/ceedling/configurator_plugins.rb
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
require 'ceedling/constants'
|
||||
|
||||
class ConfiguratorPlugins
|
||||
|
||||
constructor :stream_wrapper, :file_wrapper, :system_wrapper
|
||||
attr_reader :rake_plugins, :script_plugins
|
||||
|
||||
def setup
|
||||
@rake_plugins = []
|
||||
@script_plugins = []
|
||||
end
|
||||
|
||||
|
||||
def add_load_paths(config)
|
||||
plugin_paths = {}
|
||||
|
||||
config[:plugins][:enabled].each do |plugin|
|
||||
config[:plugins][:load_paths].each do |root|
|
||||
path = File.join(root, plugin)
|
||||
|
||||
is_script_plugin = ( not @file_wrapper.directory_listing( File.join( path, 'lib', '*.rb' ) ).empty? )
|
||||
is_rake_plugin = ( not @file_wrapper.directory_listing( File.join( path, '*.rake' ) ).empty? )
|
||||
|
||||
if is_script_plugin or is_rake_plugin
|
||||
plugin_paths[(plugin + '_path').to_sym] = path
|
||||
|
||||
if is_script_plugin
|
||||
@system_wrapper.add_load_path( File.join( path, 'lib') )
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return plugin_paths
|
||||
end
|
||||
|
||||
|
||||
# gather up and return .rake filepaths that exist on-disk
|
||||
def find_rake_plugins(config, plugin_paths)
|
||||
@rake_plugins = []
|
||||
plugins_with_path = []
|
||||
|
||||
config[:plugins][:enabled].each do |plugin|
|
||||
if path = plugin_paths[(plugin + '_path').to_sym]
|
||||
rake_plugin_path = File.join(path, "#{plugin}.rake")
|
||||
if (@file_wrapper.exist?(rake_plugin_path))
|
||||
plugins_with_path << rake_plugin_path
|
||||
@rake_plugins << plugin
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return plugins_with_path
|
||||
end
|
||||
|
||||
|
||||
# gather up and return just names of .rb classes that exist on-disk
|
||||
def find_script_plugins(config, plugin_paths)
|
||||
@script_plugins = []
|
||||
|
||||
config[:plugins][:enabled].each do |plugin|
|
||||
if path = plugin_paths[(plugin + '_path').to_sym]
|
||||
script_plugin_path = File.join(path, "lib", "#{plugin}.rb")
|
||||
|
||||
if @file_wrapper.exist?(script_plugin_path)
|
||||
@script_plugins << plugin
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return @script_plugins
|
||||
end
|
||||
|
||||
|
||||
# gather up and return configuration .yml filepaths that exist on-disk
|
||||
def find_config_plugins(config, plugin_paths)
|
||||
plugins_with_path = []
|
||||
|
||||
config[:plugins][:enabled].each do |plugin|
|
||||
if path = plugin_paths[(plugin + '_path').to_sym]
|
||||
config_plugin_path = File.join(path, "config", "#{plugin}.yml")
|
||||
|
||||
if @file_wrapper.exist?(config_plugin_path)
|
||||
plugins_with_path << config_plugin_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return plugins_with_path
|
||||
end
|
||||
|
||||
|
||||
# gather up and return default .yml filepaths that exist on-disk
|
||||
def find_plugin_defaults(config, plugin_paths)
|
||||
defaults_with_path = []
|
||||
|
||||
config[:plugins][:enabled].each do |plugin|
|
||||
if path = plugin_paths[(plugin + '_path').to_sym]
|
||||
default_path = File.join(path, 'config', 'defaults.yml')
|
||||
|
||||
if @file_wrapper.exist?(default_path)
|
||||
defaults_with_path << default_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return defaults_with_path
|
||||
end
|
||||
|
||||
end
|
@ -1,5 +1,5 @@
|
||||
|
||||
# add sort-ability to symbol so we can order keys array in hash for test-ability
|
||||
# add sort-ability to symbol so we can order keys array in hash for test-ability
|
||||
class Symbol
|
||||
include Comparable
|
||||
|
||||
@ -10,25 +10,24 @@ end
|
||||
|
||||
|
||||
class ConfiguratorSetup
|
||||
|
||||
|
||||
constructor :configurator_builder, :configurator_validator, :configurator_plugins, :stream_wrapper
|
||||
|
||||
|
||||
|
||||
|
||||
def build_project_config(config, flattened_config)
|
||||
### flesh out config
|
||||
@configurator_builder.clean(flattened_config)
|
||||
|
||||
|
||||
### add to hash values we build up from configuration & file system contents
|
||||
flattened_config.merge!(@configurator_builder.set_build_paths(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.set_force_build_filepaths(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.set_rakefile_components(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.set_library_build_info_filepaths(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.set_release_target(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.collect_project_options(flattened_config))
|
||||
|
||||
|
||||
### iterate through all entries in paths section and expand any & all globs to actual paths
|
||||
flattened_config.merge!(@configurator_builder.expand_all_path_globs(flattened_config))
|
||||
|
||||
|
||||
flattened_config.merge!(@configurator_builder.collect_vendor_paths(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.collect_source_and_include_paths(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.collect_source_include_vendor_paths(flattened_config))
|
||||
@ -38,6 +37,7 @@ class ConfiguratorSetup
|
||||
flattened_config.merge!(@configurator_builder.collect_assembly(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.collect_source(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.collect_headers(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.collect_release_existing_compilation_input(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.collect_all_existing_compilation_input(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.collect_test_and_vendor_defines(flattened_config))
|
||||
flattened_config.merge!(@configurator_builder.collect_release_and_vendor_defines(flattened_config))
|
||||
@ -47,13 +47,13 @@ class ConfiguratorSetup
|
||||
return flattened_config
|
||||
end
|
||||
|
||||
|
||||
|
||||
def build_constants_and_accessors(config, context)
|
||||
@configurator_builder.build_global_constants(config)
|
||||
@configurator_builder.build_accessor_methods(config, context)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
def validate_required_sections(config)
|
||||
validation = []
|
||||
validation << @configurator_validator.exists?(config, :project)
|
||||
@ -76,8 +76,11 @@ class ConfiguratorSetup
|
||||
def validate_paths(config)
|
||||
validation = []
|
||||
|
||||
validation << @configurator_validator.validate_filepath(config, :project, :build_root)
|
||||
validation << @configurator_validator.validate_filepath(config, :cmock, :unity_helper) if config[:cmock][:unity_helper]
|
||||
if config[:cmock][:unity_helper]
|
||||
config[:cmock][:unity_helper].each do |path|
|
||||
validation << @configurator_validator.validate_filepath_simple( path, :cmock, :unity_helper )
|
||||
end
|
||||
end
|
||||
|
||||
config[:project][:options_paths].each do |path|
|
||||
validation << @configurator_validator.validate_filepath_simple( path, :project, :options_paths )
|
||||
@ -94,7 +97,7 @@ class ConfiguratorSetup
|
||||
return false if (validation.include?(false))
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
def validate_tools(config)
|
||||
validation = []
|
||||
|
||||
@ -109,15 +112,15 @@ class ConfiguratorSetup
|
||||
end
|
||||
|
||||
def validate_plugins(config)
|
||||
missing_plugins =
|
||||
Set.new( config[:plugins][:enabled] ) -
|
||||
Set.new( @configurator_plugins.rake_plugins ) -
|
||||
missing_plugins =
|
||||
Set.new( config[:plugins][:enabled] ) -
|
||||
Set.new( @configurator_plugins.rake_plugins ) -
|
||||
Set.new( @configurator_plugins.script_plugins )
|
||||
|
||||
|
||||
missing_plugins.each do |plugin|
|
||||
@stream_wrapper.stderr_puts("ERROR: Ceedling plugin '#{plugin}' contains no rake or ruby class entry point. (Misspelled or missing files?)")
|
||||
end
|
||||
|
||||
|
||||
return ( (missing_plugins.size > 0) ? false : true )
|
||||
end
|
||||
|
@ -1,8 +1,8 @@
|
||||
require 'rubygems'
|
||||
require 'rake' # for ext()
|
||||
require 'constants'
|
||||
require 'tool_executor' # for argument replacement pattern
|
||||
require 'file_path_utils' # for glob handling class methods
|
||||
require 'ceedling/constants'
|
||||
require 'ceedling/tool_executor' # for argument replacement pattern
|
||||
require 'ceedling/file_path_utils' # for glob handling class methods
|
||||
|
||||
|
||||
class ConfiguratorValidator
|
||||
@ -78,9 +78,18 @@ class ConfiguratorValidator
|
||||
return true if (filepath =~ TOOL_EXECUTOR_ARGUMENT_REPLACEMENT_PATTERN)
|
||||
|
||||
if (not @file_wrapper.exist?(filepath))
|
||||
# no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator
|
||||
@stream_wrapper.stderr_puts("ERROR: Config filepath #{format_key_sequence(keys, hash[:depth])}['#{filepath}'] does not exist on disk.")
|
||||
return false
|
||||
|
||||
# See if we can deal with it internally.
|
||||
if GENERATED_DIR_PATH.include?(filepath)
|
||||
# we already made this directory before let's make it again.
|
||||
FileUtils.mkdir_p File.join(File.dirname(__FILE__), filepath)
|
||||
@stream_wrapper.stderr_puts("WARNING: Generated filepath #{format_key_sequence(keys, hash[:depth])}['#{filepath}'] does not exist on disk. Recreating")
|
||||
|
||||
else
|
||||
# no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator
|
||||
@stream_wrapper.stderr_puts("ERROR: Config filepath #{format_key_sequence(keys, hash[:depth])}['#{filepath}'] does not exist on disk.")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
97
test/vendor/ceedling/lib/ceedling/constants.rb
vendored
Normal file
97
test/vendor/ceedling/lib/ceedling/constants.rb
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
|
||||
class Verbosity
|
||||
SILENT = 0 # as silent as possible (though there are some messages that must be spit out)
|
||||
ERRORS = 1 # only errors
|
||||
COMPLAIN = 2 # spit out errors and warnings/notices
|
||||
NORMAL = 3 # errors, warnings/notices, standard status messages
|
||||
OBNOXIOUS = 4 # all messages including extra verbose output (used for lite debugging / verification)
|
||||
DEBUG = 5 # special extra verbose output for hardcore debugging
|
||||
end
|
||||
|
||||
|
||||
class TestResultsSanityChecks
|
||||
NONE = 0 # no sanity checking of test results
|
||||
NORMAL = 1 # perform non-problematic checks
|
||||
THOROUGH = 2 # perform checks that require inside knowledge of system workings
|
||||
end
|
||||
|
||||
|
||||
class StdErrRedirect
|
||||
NONE = :none
|
||||
AUTO = :auto
|
||||
WIN = :win
|
||||
UNIX = :unix
|
||||
TCSH = :tcsh
|
||||
end
|
||||
|
||||
|
||||
class BackgroundExec
|
||||
NONE = :none
|
||||
AUTO = :auto
|
||||
WIN = :win
|
||||
UNIX = :unix
|
||||
end
|
||||
|
||||
unless defined?(PROJECT_ROOT)
|
||||
PROJECT_ROOT = Dir.pwd()
|
||||
end
|
||||
|
||||
GENERATED_DIR_PATH = [['vendor', 'ceedling'], 'src', "test", ['test', 'support'], 'build'].each{|p| File.join(*p)}
|
||||
|
||||
EXTENSION_WIN_EXE = '.exe'
|
||||
EXTENSION_NONWIN_EXE = '.out'
|
||||
|
||||
|
||||
CEXCEPTION_ROOT_PATH = 'c_exception'
|
||||
CEXCEPTION_LIB_PATH = "#{CEXCEPTION_ROOT_PATH}/lib"
|
||||
CEXCEPTION_C_FILE = 'CException.c'
|
||||
CEXCEPTION_H_FILE = 'CException.h'
|
||||
|
||||
UNITY_ROOT_PATH = 'unity'
|
||||
UNITY_LIB_PATH = "#{UNITY_ROOT_PATH}/src"
|
||||
UNITY_C_FILE = 'unity.c'
|
||||
UNITY_H_FILE = 'unity.h'
|
||||
UNITY_INTERNALS_H_FILE = 'unity_internals.h'
|
||||
|
||||
CMOCK_ROOT_PATH = 'cmock'
|
||||
CMOCK_LIB_PATH = "#{CMOCK_ROOT_PATH}/src"
|
||||
CMOCK_C_FILE = 'cmock.c'
|
||||
CMOCK_H_FILE = 'cmock.h'
|
||||
|
||||
|
||||
DEFAULT_CEEDLING_MAIN_PROJECT_FILE = 'project.yml' unless defined?(DEFAULT_CEEDLING_MAIN_PROJECT_FILE) # main project file
|
||||
DEFAULT_CEEDLING_USER_PROJECT_FILE = 'user.yml' unless defined?(DEFAULT_CEEDLING_USER_PROJECT_FILE) # supplemental user config file
|
||||
|
||||
INPUT_CONFIGURATION_CACHE_FILE = 'input.yml' unless defined?(INPUT_CONFIGURATION_CACHE_FILE) # input configuration file dump
|
||||
|
||||
|
||||
TEST_ROOT_NAME = 'test' unless defined?(TEST_ROOT_NAME)
|
||||
TEST_TASK_ROOT = TEST_ROOT_NAME + ':' unless defined?(TEST_TASK_ROOT)
|
||||
TEST_SYM = TEST_ROOT_NAME.to_sym unless defined?(TEST_SYM)
|
||||
|
||||
RELEASE_ROOT_NAME = 'release' unless defined?(RELEASE_ROOT_NAME)
|
||||
RELEASE_TASK_ROOT = RELEASE_ROOT_NAME + ':' unless defined?(RELEASE_TASK_ROOT)
|
||||
RELEASE_SYM = RELEASE_ROOT_NAME.to_sym unless defined?(RELEASE_SYM)
|
||||
|
||||
REFRESH_ROOT_NAME = 'refresh' unless defined?(REFRESH_ROOT_NAME)
|
||||
REFRESH_TASK_ROOT = REFRESH_ROOT_NAME + ':' unless defined?(REFRESH_TASK_ROOT)
|
||||
REFRESH_SYM = REFRESH_ROOT_NAME.to_sym unless defined?(REFRESH_SYM)
|
||||
|
||||
UTILS_ROOT_NAME = 'utils' unless defined?(UTILS_ROOT_NAME)
|
||||
UTILS_TASK_ROOT = UTILS_ROOT_NAME + ':' unless defined?(UTILS_TASK_ROOT)
|
||||
UTILS_SYM = UTILS_ROOT_NAME.to_sym unless defined?(UTILS_SYM)
|
||||
|
||||
OPERATION_COMPILE_SYM = :compile unless defined?(OPERATION_COMPILE_SYM)
|
||||
OPERATION_ASSEMBLE_SYM = :assemble unless defined?(OPERATION_ASSEMBLE_SYM)
|
||||
OPERATION_LINK_SYM = :link unless defined?(OPERATION_LINK_SYM)
|
||||
|
||||
|
||||
RUBY_STRING_REPLACEMENT_PATTERN = /#\{.+\}/
|
||||
RUBY_EVAL_REPLACEMENT_PATTERN = /^\{(.+)\}$/
|
||||
TOOL_EXECUTOR_ARGUMENT_REPLACEMENT_PATTERN = /(\$\{(\d+)\})/
|
||||
TEST_STDOUT_STATISTICS_PATTERN = /\n-+\s*(\d+)\s+Tests\s+(\d+)\s+Failures\s+(\d+)\s+Ignored\s+(OK|FAIL)\s*/i
|
||||
|
||||
NULL_FILE_PATH = '/dev/null'
|
||||
|
||||
TESTS_BASE_PATH = TEST_ROOT_NAME
|
||||
RELEASE_BASE_PATH = RELEASE_ROOT_NAME
|
@ -1,7 +1,10 @@
|
||||
require 'constants'
|
||||
require 'system_wrapper'
|
||||
require 'file_path_utils'
|
||||
require 'ceedling/constants'
|
||||
require 'ceedling/system_wrapper'
|
||||
require 'ceedling/file_path_utils'
|
||||
|
||||
#this should be defined already, but not always during system specs
|
||||
CEEDLING_VENDOR = File.expand_path(File.dirname(__FILE__) + '/../../vendor') unless defined? CEEDLING_VENDOR
|
||||
CEEDLING_PLUGINS = [] unless defined? CEEDLING_PLUGINS
|
||||
|
||||
DEFAULT_TEST_COMPILER_TOOL = {
|
||||
:executable => FilePathUtils.os_executable_ext('gcc').freeze,
|
||||
@ -14,9 +17,12 @@ DEFAULT_TEST_COMPILER_TOOL = {
|
||||
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
|
||||
{"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
|
||||
"-DGNU_COMPILER".freeze,
|
||||
"-g".freeze,
|
||||
"-c \"${1}\"".freeze,
|
||||
"-o \"${2}\"".freeze,
|
||||
# gcc's list file output options are complex; no use of ${3} parameter in default config
|
||||
# gcc's list file output options are complex; no use of ${3} parameter in default config
|
||||
"-MMD".freeze,
|
||||
"-MF \"${4}\"".freeze,
|
||||
].freeze
|
||||
}
|
||||
|
||||
@ -29,9 +35,11 @@ DEFAULT_TEST_LINKER_TOOL = {
|
||||
:arguments => [
|
||||
"\"${1}\"".freeze,
|
||||
"-o \"${2}\"".freeze,
|
||||
"".freeze,
|
||||
"${4}".freeze
|
||||
].freeze
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_TEST_FIXTURE_TOOL = {
|
||||
:executable => '${1}'.freeze,
|
||||
:name => 'default_test_fixture'.freeze,
|
||||
@ -41,25 +49,26 @@ DEFAULT_TEST_FIXTURE_TOOL = {
|
||||
:arguments => [].freeze
|
||||
}
|
||||
|
||||
|
||||
|
||||
DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL = {
|
||||
:executable => FilePathUtils.os_executable_ext('cpp').freeze,
|
||||
:executable => FilePathUtils.os_executable_ext('gcc').freeze,
|
||||
:name => 'default_test_includes_preprocessor'.freeze,
|
||||
:stderr_redirect => StdErrRedirect::NONE.freeze,
|
||||
:background_exec => BackgroundExec::NONE.freeze,
|
||||
:optional => false.freeze,
|
||||
:arguments => [
|
||||
'-E'.freeze, # OSX clang
|
||||
'-MM'.freeze,
|
||||
'-MG'.freeze,
|
||||
# avoid some possibility of deep system lib header file complications by omitting vendor paths
|
||||
# if cpp is run on *nix system, escape spaces in paths; if cpp on windows just use the paths collection as is
|
||||
{"-I\"$\"" => "{SystemWrapper.windows? ? COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE : COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE.map{|path| path.gsub(\/ \/, \'\\\\ \') }}"}.freeze,
|
||||
# {"-I\"$\"" => "{SystemWrapper.windows? ? COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE : COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE.map{|path| path.gsub(\/ \/, \'\\\\ \') }}"}.freeze,
|
||||
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze,
|
||||
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
|
||||
{"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
|
||||
{"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze,
|
||||
"-DGNU_PREPROCESSOR".freeze,
|
||||
"-DGNU_COMPILER".freeze, # OSX clang
|
||||
'-w'.freeze,
|
||||
'-nostdinc'.freeze,
|
||||
# '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX
|
||||
"\"${1}\"".freeze
|
||||
].freeze
|
||||
}
|
||||
@ -76,12 +85,20 @@ DEFAULT_TEST_FILE_PREPROCESSOR_TOOL = {
|
||||
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
|
||||
{"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
|
||||
{"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze,
|
||||
"-DGNU_PREPROCESSOR".freeze,
|
||||
"-DGNU_COMPILER".freeze,
|
||||
# '-nostdinc'.freeze, # disabled temporarily due to stdio access violations on OSX
|
||||
"\"${1}\"".freeze,
|
||||
"-o \"${2}\"".freeze
|
||||
].freeze
|
||||
}
|
||||
|
||||
# Disable the -MD flag for OSX LLVM Clang, since unsupported
|
||||
if RUBY_PLATFORM =~ /darwin/ && `gcc --version 2> /dev/null` =~ /Apple LLVM version .* \(clang/m # OSX w/LLVM Clang
|
||||
MD_FLAG = '' # Clang doesn't support the -MD flag
|
||||
else
|
||||
MD_FLAG = '-MD'
|
||||
end
|
||||
|
||||
DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL = {
|
||||
:executable => FilePathUtils.os_executable_ext('gcc').freeze,
|
||||
:name => 'default_test_dependencies_generator'.freeze,
|
||||
@ -89,17 +106,19 @@ DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL = {
|
||||
:background_exec => BackgroundExec::NONE.freeze,
|
||||
:optional => false.freeze,
|
||||
:arguments => [
|
||||
'-E'.freeze,
|
||||
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze,
|
||||
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
|
||||
{"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
|
||||
{"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze,
|
||||
"-DGNU_PREPROCESSOR".freeze,
|
||||
{"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
|
||||
{"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze,
|
||||
"-DGNU_COMPILER".freeze,
|
||||
"-MT \"${3}\"".freeze,
|
||||
'-MM'.freeze,
|
||||
'-MD'.freeze,
|
||||
MD_FLAG.freeze,
|
||||
'-MG'.freeze,
|
||||
"-MF \"${2}\"".freeze,
|
||||
"-c \"${1}\"".freeze,
|
||||
# '-nostdinc'.freeze,
|
||||
].freeze
|
||||
}
|
||||
|
||||
@ -110,17 +129,19 @@ DEFAULT_RELEASE_DEPENDENCIES_GENERATOR_TOOL = {
|
||||
:background_exec => BackgroundExec::NONE.freeze,
|
||||
:optional => false.freeze,
|
||||
:arguments => [
|
||||
'-E'.freeze,
|
||||
{"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze,
|
||||
{"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze,
|
||||
{"-D$" => 'COLLECTION_DEFINES_RELEASE_AND_VENDOR'}.freeze,
|
||||
{"-D$" => 'DEFINES_RELEASE_PREPROCESS'}.freeze,
|
||||
"-DGNU_PREPROCESSOR".freeze,
|
||||
"-DGNU_COMPILER".freeze,
|
||||
"-MT \"${3}\"".freeze,
|
||||
'-MM'.freeze,
|
||||
'-MD'.freeze,
|
||||
MD_FLAG.freeze,
|
||||
'-MG'.freeze,
|
||||
"-MF \"${2}\"".freeze,
|
||||
"-c \"${1}\"".freeze,
|
||||
# '-nostdinc'.freeze,
|
||||
].freeze
|
||||
}
|
||||
|
||||
@ -138,7 +159,9 @@ DEFAULT_RELEASE_COMPILER_TOOL = {
|
||||
"-DGNU_COMPILER".freeze,
|
||||
"-c \"${1}\"".freeze,
|
||||
"-o \"${2}\"".freeze,
|
||||
# gcc's list file output options are complex; no use of ${3} parameter in default config
|
||||
# gcc's list file output options are complex; no use of ${3} parameter in default config
|
||||
"-MMD".freeze,
|
||||
"-MF \"${4}\"".freeze,
|
||||
].freeze
|
||||
}
|
||||
|
||||
@ -164,10 +187,12 @@ DEFAULT_RELEASE_LINKER_TOOL = {
|
||||
:arguments => [
|
||||
"\"${1}\"".freeze,
|
||||
"-o \"${2}\"".freeze,
|
||||
"".freeze,
|
||||
"${4}".freeze
|
||||
].freeze
|
||||
}
|
||||
|
||||
|
||||
|
||||
DEFAULT_TOOLS_TEST = {
|
||||
:tools => {
|
||||
:test_compiler => DEFAULT_TEST_COMPILER_TOOL,
|
||||
@ -175,7 +200,7 @@ DEFAULT_TOOLS_TEST = {
|
||||
:test_fixture => DEFAULT_TEST_FIXTURE_TOOL,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_TOOLS_TEST_PREPROCESSORS = {
|
||||
:tools => {
|
||||
:test_includes_preprocessor => DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL,
|
||||
@ -209,7 +234,7 @@ DEFAULT_TOOLS_RELEASE_DEPENDENCIES = {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
DEFAULT_RELEASE_TARGET_NAME = 'project'
|
||||
|
||||
DEFAULT_CEEDLING_CONFIG = {
|
||||
@ -221,6 +246,7 @@ DEFAULT_CEEDLING_CONFIG = {
|
||||
:test_threads => 1,
|
||||
:use_test_preprocessor => false,
|
||||
:use_deep_dependencies => false,
|
||||
:generate_deep_dependencies => true, # only applicable if use_deep_dependencies is true
|
||||
:test_file_prefix => 'test_',
|
||||
:options_paths => [],
|
||||
:release_build => false,
|
||||
@ -240,7 +266,7 @@ DEFAULT_CEEDLING_CONFIG = {
|
||||
:test_toolchain_include => [],
|
||||
:release_toolchain_include => [],
|
||||
},
|
||||
|
||||
|
||||
:files => {
|
||||
:test => [],
|
||||
:source => [],
|
||||
@ -248,22 +274,30 @@ DEFAULT_CEEDLING_CONFIG = {
|
||||
:support => [],
|
||||
:include => [],
|
||||
},
|
||||
|
||||
|
||||
# unlike other top-level entries, environment's value is an array to preserve order
|
||||
:environment => [
|
||||
# when evaluated, this provides wider text field for rake task comments
|
||||
{:rake_columns => '120'},
|
||||
],
|
||||
|
||||
|
||||
:defines => {
|
||||
:test => [],
|
||||
:test_preprocess => [],
|
||||
:release => [],
|
||||
:release_preprocess => [],
|
||||
:use_test_definition => false,
|
||||
},
|
||||
|
||||
|
||||
:libraries => {
|
||||
:test => [],
|
||||
:test_preprocess => [],
|
||||
:release => [],
|
||||
:release_preprocess => [],
|
||||
},
|
||||
|
||||
:flags => {},
|
||||
|
||||
|
||||
:extension => {
|
||||
:header => '.h',
|
||||
:source => '.c',
|
||||
@ -278,14 +312,18 @@ DEFAULT_CEEDLING_CONFIG = {
|
||||
},
|
||||
|
||||
:unity => {
|
||||
:vendor_path => CEEDLING_VENDOR,
|
||||
:defines => []
|
||||
},
|
||||
|
||||
:cmock => {
|
||||
:defines => []
|
||||
:vendor_path => CEEDLING_VENDOR,
|
||||
:defines => [],
|
||||
:includes => []
|
||||
},
|
||||
|
||||
:cexception => {
|
||||
:vendor_path => CEEDLING_VENDOR,
|
||||
:defines => []
|
||||
},
|
||||
|
||||
@ -301,7 +339,7 @@ DEFAULT_CEEDLING_CONFIG = {
|
||||
# (these can be overridden in project file to add arguments to tools without totally redefining tools)
|
||||
:test_compiler => { :arguments => [] },
|
||||
:test_linker => { :arguments => [] },
|
||||
:test_fixture => {
|
||||
:test_fixture => {
|
||||
:arguments => [],
|
||||
:link_objects => [], # compiled object files to always be linked in (e.g. cmock.o if using mocks)
|
||||
},
|
||||
@ -314,12 +352,12 @@ DEFAULT_CEEDLING_CONFIG = {
|
||||
:release_dependencies_generator => { :arguments => [] },
|
||||
|
||||
:plugins => {
|
||||
:load_paths => [],
|
||||
:load_paths => CEEDLING_PLUGINS,
|
||||
:enabled => [],
|
||||
}
|
||||
}.freeze
|
||||
|
||||
|
||||
|
||||
DEFAULT_TESTS_RESULTS_REPORT_TEMPLATE = %q{
|
||||
% ignored = hash[:results][:counts][:ignored]
|
||||
% failed = hash[:results][:counts][:failed]
|
||||
@ -327,8 +365,17 @@ DEFAULT_TESTS_RESULTS_REPORT_TEMPLATE = %q{
|
||||
% header_prepend = ((hash[:header].length > 0) ? "#{hash[:header]}: " : '')
|
||||
% banner_width = 25 + header_prepend.length # widest message
|
||||
|
||||
% if (stdout_count > 0)
|
||||
<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'TEST OUTPUT')%>
|
||||
% hash[:results][:stdout].each do |string|
|
||||
% string[:collection].each do |item|
|
||||
<%=string[:source][:path]%><%=File::SEPARATOR%><%=string[:source][:file]%>: "<%=item%>"
|
||||
% end
|
||||
% end
|
||||
|
||||
% end
|
||||
% if (ignored > 0)
|
||||
<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'IGNORED UNIT TEST SUMMARY')%>
|
||||
<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'IGNORED TEST SUMMARY')%>
|
||||
% hash[:results][:ignores].each do |ignore|
|
||||
% ignore[:collection].each do |item|
|
||||
<%=ignore[:source][:path]%><%=File::SEPARATOR%><%=ignore[:source][:file]%>:<%=item[:line]%>:<%=item[:test]%>
|
||||
@ -342,7 +389,7 @@ DEFAULT_TESTS_RESULTS_REPORT_TEMPLATE = %q{
|
||||
|
||||
% end
|
||||
% if (failed > 0)
|
||||
<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + "\e[31m" 'FAILED UNIT TEST SUMMARY')%>
|
||||
<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'FAILED TEST SUMMARY')%>
|
||||
% hash[:results][:failures].each do |failure|
|
||||
% failure[:collection].each do |item|
|
||||
<%=failure[:source][:path]%><%=File::SEPARATOR%><%=failure[:source][:file]%>:<%=item[:line]%>:<%=item[:test]%>
|
||||
@ -354,19 +401,10 @@ DEFAULT_TESTS_RESULTS_REPORT_TEMPLATE = %q{
|
||||
% end
|
||||
% end
|
||||
|
||||
% end
|
||||
% if (stdout_count > 0)
|
||||
<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'UNIT TEST OTHER OUTPUT')%>
|
||||
% hash[:results][:stdout].each do |string|
|
||||
% string[:collection].each do |item|
|
||||
<%=string[:source][:path]%><%=File::SEPARATOR%><%=string[:source][:file]%>: "<%=item%>"
|
||||
% end
|
||||
% end
|
||||
|
||||
% end
|
||||
% total_string = hash[:results][:counts][:total].to_s
|
||||
% format_string = "%#{total_string.length}i"
|
||||
<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'OVERALL UNIT TEST SUMMARY')%>
|
||||
<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'OVERALL TEST SUMMARY')%>
|
||||
% if (hash[:results][:counts][:total] > 0)
|
||||
TESTED: <%=hash[:results][:counts][:total].to_s%>
|
||||
PASSED: <%=sprintf(format_string, hash[:results][:counts][:passed])%>
|
@ -4,21 +4,24 @@ class Dependinator
|
||||
constructor :configurator, :project_config_manager, :test_includes_extractor, :file_path_utils, :rake_wrapper, :file_wrapper
|
||||
|
||||
def touch_force_rebuild_files
|
||||
@file_wrapper.touch( @configurator.project_test_force_rebuild_filepath )
|
||||
@file_wrapper.touch( @configurator.project_test_force_rebuild_filepath )
|
||||
@file_wrapper.touch( @configurator.project_release_force_rebuild_filepath ) if (@configurator.project_release_build)
|
||||
end
|
||||
|
||||
|
||||
|
||||
def load_release_object_deep_dependencies(dependencies_list)
|
||||
dependencies_list.each { |dependencies_file| @rake_wrapper.load_dependencies( dependencies_file ) }
|
||||
dependencies_list.each do |dependencies_file|
|
||||
if File.exists?(dependencies_file)
|
||||
@rake_wrapper.load_dependencies( dependencies_file )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def enhance_release_file_dependencies(files)
|
||||
files.each do |filepath|
|
||||
files.each do |filepath|
|
||||
@rake_wrapper[filepath].enhance( [@configurator.project_release_force_rebuild_filepath] ) if (@project_config_manager.release_config_changed)
|
||||
@rake_wrapper[filepath].enhance( [@configurator.ceedling_build_info_filepath] )
|
||||
end
|
||||
end
|
||||
|
||||
@ -26,20 +29,22 @@ class Dependinator
|
||||
|
||||
def load_test_object_deep_dependencies(files_list)
|
||||
dependencies_list = @file_path_utils.form_test_dependencies_filelist(files_list)
|
||||
dependencies_list.each { |dependencies_file| @rake_wrapper.load_dependencies(dependencies_file) }
|
||||
dependencies_list.each do |dependencies_file|
|
||||
if File.exists?(dependencies_file)
|
||||
@rake_wrapper.load_dependencies(dependencies_file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def enhance_runner_dependencies(runner_filepath)
|
||||
@rake_wrapper[runner_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
|
||||
@rake_wrapper[runner_filepath].enhance( [@configurator.ceedling_build_info_filepath] )
|
||||
end
|
||||
|
||||
|
||||
|
||||
def enhance_shallow_include_lists_dependencies(include_lists)
|
||||
include_lists.each do |include_list_filepath|
|
||||
@rake_wrapper[include_list_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
|
||||
@rake_wrapper[include_list_filepath].enhance( [@configurator.ceedling_build_info_filepath] )
|
||||
end
|
||||
end
|
||||
|
||||
@ -47,7 +52,6 @@ class Dependinator
|
||||
def enhance_preprocesed_file_dependencies(files)
|
||||
files.each do |filepath|
|
||||
@rake_wrapper[filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
|
||||
@rake_wrapper[filepath].enhance( [@configurator.ceedling_build_info_filepath] )
|
||||
end
|
||||
end
|
||||
|
||||
@ -56,9 +60,7 @@ class Dependinator
|
||||
# if input configuration or ceedling changes, make sure these guys get rebuilt
|
||||
mocks_list.each do |mock_filepath|
|
||||
@rake_wrapper[mock_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
|
||||
@rake_wrapper[mock_filepath].enhance( [@configurator.cmock_unity_helper] ) if (@configurator.cmock_unity_helper)
|
||||
@rake_wrapper[mock_filepath].enhance( [@configurator.ceedling_build_info_filepath] )
|
||||
@rake_wrapper[mock_filepath].enhance( [@configurator.cmock_build_info_filepath] )
|
||||
@rake_wrapper[mock_filepath].enhance( @configurator.cmock_unity_helper ) if (@configurator.cmock_unity_helper)
|
||||
end
|
||||
end
|
||||
|
||||
@ -66,7 +68,6 @@ class Dependinator
|
||||
def enhance_dependencies_dependencies(dependencies)
|
||||
dependencies.each do |dependencies_filepath|
|
||||
@rake_wrapper[dependencies_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
|
||||
@rake_wrapper[dependencies_filepath].enhance( [@configurator.ceedling_build_info_filepath] )
|
||||
end
|
||||
end
|
||||
|
||||
@ -74,14 +75,12 @@ class Dependinator
|
||||
def enhance_test_build_object_dependencies(objects)
|
||||
objects.each do |object_filepath|
|
||||
@rake_wrapper[object_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
|
||||
@rake_wrapper[object_filepath].enhance( [@configurator.ceedling_build_info_filepath] )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def enhance_results_dependencies(result_filepath)
|
||||
@rake_wrapper[result_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
|
||||
@rake_wrapper[result_filepath].enhance( [@configurator.ceedling_build_info_filepath] )
|
||||
end
|
||||
|
||||
|
@ -1,14 +1,17 @@
|
||||
require 'rubygems'
|
||||
require 'rake' # for adding ext() method to string
|
||||
require 'thread'
|
||||
|
||||
|
||||
class FileFinder
|
||||
SEMAPHORE = Mutex.new
|
||||
|
||||
constructor :configurator, :file_finder_helper, :cacheinator, :file_path_utils, :file_wrapper, :yaml_wrapper
|
||||
|
||||
def prepare_search_sources
|
||||
@all_test_source_and_header_file_collection =
|
||||
@all_test_source_and_header_file_collection =
|
||||
@configurator.collection_all_tests +
|
||||
@configurator.collection_all_source +
|
||||
@configurator.collection_all_source +
|
||||
@configurator.collection_all_headers
|
||||
end
|
||||
|
||||
@ -25,19 +28,19 @@ class FileFinder
|
||||
def find_header_input_for_mock_file(mock_file)
|
||||
found_path = find_header_file(mock_file)
|
||||
mock_input = found_path
|
||||
|
||||
|
||||
if (@configurator.project_use_test_preprocessor)
|
||||
mock_input = @cacheinator.diff_cached_test_file( @file_path_utils.form_preprocessed_file_filepath( found_path ) )
|
||||
end
|
||||
|
||||
|
||||
return mock_input
|
||||
end
|
||||
|
||||
|
||||
|
||||
def find_source_from_test(test, complain)
|
||||
test_prefix = @configurator.project_test_file_prefix
|
||||
source_paths = @configurator.collection_all_source
|
||||
|
||||
|
||||
source = File.basename(test).sub(/#{test_prefix}/, '')
|
||||
|
||||
# we don't blow up if a test file has no corresponding source file
|
||||
@ -49,31 +52,31 @@ class FileFinder
|
||||
extension_source = @configurator.extension_source
|
||||
|
||||
test_file = File.basename(runner_path).sub(/#{@configurator.test_runner_file_suffix}#{'\\'+extension_source}/, extension_source)
|
||||
|
||||
|
||||
found_path = @file_finder_helper.find_file_in_collection(test_file, @configurator.collection_all_tests, :error)
|
||||
|
||||
return found_path
|
||||
end
|
||||
|
||||
|
||||
|
||||
def find_test_input_for_runner_file(runner_path)
|
||||
found_path = find_test_from_runner_path(runner_path)
|
||||
runner_input = found_path
|
||||
|
||||
|
||||
if (@configurator.project_use_test_preprocessor)
|
||||
runner_input = @cacheinator.diff_cached_test_file( @file_path_utils.form_preprocessed_file_filepath( found_path ) )
|
||||
end
|
||||
|
||||
|
||||
return runner_input
|
||||
end
|
||||
|
||||
|
||||
|
||||
def find_test_from_file_path(file_path)
|
||||
test_file = File.basename(file_path).ext(@configurator.extension_source)
|
||||
|
||||
|
||||
found_path = @file_finder_helper.find_file_in_collection(test_file, @configurator.collection_all_tests, :error)
|
||||
|
||||
return found_path
|
||||
|
||||
return found_path
|
||||
end
|
||||
|
||||
|
||||
@ -81,52 +84,66 @@ class FileFinder
|
||||
file = File.basename(file_path)
|
||||
return @file_finder_helper.find_file_in_collection(file, @all_test_source_and_header_file_collection, :error)
|
||||
end
|
||||
|
||||
|
||||
def find_compilation_input_file(file_path, complain=:error)
|
||||
|
||||
|
||||
def find_compilation_input_file(file_path, complain=:error, release=false)
|
||||
found_file = nil
|
||||
|
||||
|
||||
source_file = File.basename(file_path).ext(@configurator.extension_source)
|
||||
|
||||
# We only collect files that already exist when we start up.
|
||||
# FileLists can produce undesired results for dynamically generated files depending on when they're accessed.
|
||||
# So collect mocks and runners separately and right now.
|
||||
if (source_file =~ /#{@configurator.test_runner_file_suffix}/)
|
||||
found_file =
|
||||
@file_finder_helper.find_file_in_collection(
|
||||
source_file,
|
||||
@file_wrapper.directory_listing( File.join(@configurator.project_test_runners_path, '*') ),
|
||||
complain)
|
||||
|
||||
elsif (@configurator.project_use_mocks and (source_file =~ /#{@configurator.cmock_mock_prefix}/))
|
||||
found_file =
|
||||
@file_finder_helper.find_file_in_collection(
|
||||
source_file,
|
||||
@file_wrapper.directory_listing( File.join(@configurator.cmock_mock_path, '*') ),
|
||||
complain)
|
||||
|
||||
else
|
||||
found_file =
|
||||
@file_finder_helper.find_file_in_collection(
|
||||
source_file,
|
||||
@configurator.collection_all_existing_compilation_input,
|
||||
complain)
|
||||
end
|
||||
SEMAPHORE.synchronize {
|
||||
|
||||
if (source_file =~ /#{@configurator.test_runner_file_suffix}/)
|
||||
found_file =
|
||||
@file_finder_helper.find_file_in_collection(
|
||||
source_file,
|
||||
@file_wrapper.directory_listing( File.join(@configurator.project_test_runners_path, '*') ),
|
||||
complain)
|
||||
|
||||
elsif (@configurator.project_use_mocks and (source_file =~ /#{@configurator.cmock_mock_prefix}/))
|
||||
found_file =
|
||||
@file_finder_helper.find_file_in_collection(
|
||||
source_file,
|
||||
@file_wrapper.directory_listing( File.join(@configurator.cmock_mock_path, '*') ),
|
||||
complain)
|
||||
|
||||
elsif release
|
||||
found_file =
|
||||
@file_finder_helper.find_file_in_collection(
|
||||
source_file,
|
||||
@configurator.collection_release_existing_compilation_input,
|
||||
complain)
|
||||
else
|
||||
temp_complain = (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY) ? :ignore : complain
|
||||
found_file =
|
||||
@file_finder_helper.find_file_in_collection(
|
||||
source_file,
|
||||
@configurator.collection_all_existing_compilation_input,
|
||||
temp_complain)
|
||||
found_file ||= find_assembly_file(file_path, false) if (defined?(TEST_BUILD_USE_ASSEMBLY) && TEST_BUILD_USE_ASSEMBLY)
|
||||
end
|
||||
}
|
||||
return found_file
|
||||
end
|
||||
|
||||
|
||||
|
||||
def find_source_file(file_path, complain)
|
||||
source_file = File.basename(file_path).ext(@configurator.extension_source)
|
||||
return @file_finder_helper.find_file_in_collection(source_file, @configurator.collection_all_source, complain)
|
||||
end
|
||||
|
||||
|
||||
def find_assembly_file(file_path)
|
||||
|
||||
def find_assembly_file(file_path, complain = :error)
|
||||
assembly_file = File.basename(file_path).ext(@configurator.extension_assembly)
|
||||
return @file_finder_helper.find_file_in_collection(assembly_file, @configurator.collection_all_assembly, :error)
|
||||
return @file_finder_helper.find_file_in_collection(assembly_file, @configurator.collection_all_assembly, complain)
|
||||
end
|
||||
|
||||
def find_file_from_list(file_path, file_list, complain)
|
||||
return @file_finder_helper.find_file_in_collection(file_path, file_list, complain)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,5 +1,5 @@
|
||||
require 'fileutils'
|
||||
require 'constants' # for Verbosity enumeration
|
||||
require 'ceedling/constants' # for Verbosity enumeration
|
||||
|
||||
class FileFinderHelper
|
||||
|
@ -1,7 +1,7 @@
|
||||
require 'rubygems'
|
||||
require 'rake' # for ext()
|
||||
require 'fileutils'
|
||||
require 'system_wrapper'
|
||||
require 'ceedling/system_wrapper'
|
||||
|
||||
# global utility methods (for plugins, project files, etc.)
|
||||
def ceedling_form_filepath(destination_path, original_filepath, new_extension=nil)
|
||||
@ -19,11 +19,10 @@ class FilePathUtils
|
||||
|
||||
######### class methods ##########
|
||||
|
||||
# standardize path to use '/' path separator & begin with './' & have no trailing path separator
|
||||
# standardize path to use '/' path separator & have no trailing path separator
|
||||
def self.standardize(path)
|
||||
path.strip!
|
||||
path.gsub!(/\\/, '/')
|
||||
path.gsub!(/^((\+|-):)?\.\//, '')
|
||||
path.chomp!('/')
|
||||
return path
|
||||
end
|
||||
@ -37,20 +36,20 @@ class FilePathUtils
|
||||
# note: slightly different than File.dirname in that /files/foo remains /files/foo and does not become /files
|
||||
def self.extract_path(path)
|
||||
path = path.sub(/^(\+|-):/, '')
|
||||
|
||||
|
||||
# find first occurrence of path separator followed by directory glob specifier: *, ?, {, }, [, ]
|
||||
find_index = (path =~ GLOB_MATCHER)
|
||||
|
||||
|
||||
# no changes needed (lop off final path separator)
|
||||
return path.chomp('/') if (find_index.nil?)
|
||||
|
||||
|
||||
# extract up to first glob specifier
|
||||
path = path[0..(find_index-1)]
|
||||
|
||||
|
||||
# lop off everything up to and including final path separator
|
||||
find_index = path.rindex('/')
|
||||
return path[0..(find_index-1)] if (not find_index.nil?)
|
||||
|
||||
|
||||
# return string up to first glob specifier if no path separator found
|
||||
return path
|
||||
end
|
||||
@ -59,12 +58,12 @@ class FilePathUtils
|
||||
def self.add_path?(path)
|
||||
return (path =~ /^-:/).nil?
|
||||
end
|
||||
|
||||
|
||||
# get path (and glob) lopping off optional +: / -: prefixed aggregation modifiers
|
||||
def self.extract_path_no_aggregation_operators(path)
|
||||
return path.sub(/^(\+|-):/, '')
|
||||
end
|
||||
|
||||
|
||||
# all the globs that may be in a path string work fine with one exception;
|
||||
# to recurse through all subdirectories, the glob is dir/**/** but our paths use
|
||||
# convention of only dir/**
|
||||
@ -73,25 +72,21 @@ class FilePathUtils
|
||||
return path + '/**'
|
||||
end
|
||||
|
||||
def self.form_ceedling_vendor_path(*filepaths)
|
||||
return File.join( CEEDLING_VENDOR, filepaths )
|
||||
end
|
||||
|
||||
######### instance methods ##########
|
||||
|
||||
def form_temp_path(filepath, prefix='')
|
||||
return File.join( @configurator.project_temp_path, prefix + File.basename(filepath) )
|
||||
return File.join( @configurator.project_temp_path, prefix + File.basename(filepath) )
|
||||
end
|
||||
|
||||
|
||||
### release ###
|
||||
def form_release_build_cache_path(filepath)
|
||||
return File.join( @configurator.project_release_build_cache_path, File.basename(filepath) )
|
||||
return File.join( @configurator.project_release_build_cache_path, File.basename(filepath) )
|
||||
end
|
||||
|
||||
|
||||
def form_release_dependencies_filepath(filepath)
|
||||
return File.join( @configurator.project_release_dependencies_path, File.basename(filepath).ext(@configurator.extension_dependencies) )
|
||||
end
|
||||
|
||||
|
||||
def form_release_build_c_object_filepath(filepath)
|
||||
return File.join( @configurator.project_release_build_output_c_path, File.basename(filepath).ext(@configurator.extension_object) )
|
||||
end
|
||||
@ -111,16 +106,20 @@ class FilePathUtils
|
||||
def form_release_build_c_list_filepath(filepath)
|
||||
return File.join( @configurator.project_release_build_output_c_path, File.basename(filepath).ext(@configurator.extension_list) )
|
||||
end
|
||||
|
||||
|
||||
def form_release_dependencies_filelist(files)
|
||||
return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_release_dependencies_path}/%n#{@configurator.extension_dependencies}")
|
||||
end
|
||||
|
||||
|
||||
### tests ###
|
||||
def form_test_build_cache_path(filepath)
|
||||
return File.join( @configurator.project_test_build_cache_path, File.basename(filepath) )
|
||||
return File.join( @configurator.project_test_build_cache_path, File.basename(filepath) )
|
||||
end
|
||||
|
||||
|
||||
def form_test_dependencies_filepath(filepath)
|
||||
return File.join( @configurator.project_test_dependencies_path, File.basename(filepath).ext(@configurator.extension_dependencies) )
|
||||
end
|
||||
|
||||
def form_pass_results_filepath(filepath)
|
||||
return File.join( @configurator.project_test_results_path, File.basename(filepath).ext(@configurator.extension_testpass) )
|
||||
end
|
||||
@ -138,15 +137,19 @@ class FilePathUtils
|
||||
end
|
||||
|
||||
def form_runner_object_filepath_from_test(filepath)
|
||||
return (form_test_build_object_filepath(filepath)).sub(/(#{@configurator.extension_object})$/, "#{@configurator.test_runner_file_suffix}\\1")
|
||||
return (form_test_build_c_object_filepath(filepath)).sub(/(#{@configurator.extension_object})$/, "#{@configurator.test_runner_file_suffix}\\1")
|
||||
end
|
||||
|
||||
def form_test_build_object_filepath(filepath)
|
||||
return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_object) )
|
||||
def form_test_build_c_object_filepath(filepath)
|
||||
return File.join( @configurator.project_test_build_output_c_path, File.basename(filepath).ext(@configurator.extension_object) )
|
||||
end
|
||||
|
||||
def form_test_build_asm_object_filepath(filepath)
|
||||
return File.join( @configurator.project_test_build_output_asm_path, File.basename(filepath).ext(@configurator.extension_object) )
|
||||
end
|
||||
|
||||
def form_test_executable_filepath(filepath)
|
||||
return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_executable) )
|
||||
return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_executable) )
|
||||
end
|
||||
|
||||
def form_test_build_map_filepath(filepath)
|
||||
@ -158,32 +161,40 @@ class FilePathUtils
|
||||
end
|
||||
|
||||
def form_preprocessed_file_filepath(filepath)
|
||||
return File.join( @configurator.project_test_preprocess_files_path, File.basename(filepath) )
|
||||
return File.join( @configurator.project_test_preprocess_files_path, File.basename(filepath) )
|
||||
end
|
||||
|
||||
def form_preprocessed_includes_list_filepath(filepath)
|
||||
return File.join( @configurator.project_test_preprocess_includes_path, File.basename(filepath) )
|
||||
return File.join( @configurator.project_test_preprocess_includes_path, File.basename(filepath) )
|
||||
end
|
||||
|
||||
def form_test_build_objects_filelist(sources)
|
||||
return (@file_wrapper.instantiate_file_list(sources)).pathmap("#{@configurator.project_test_build_output_path}/%n#{@configurator.extension_object}")
|
||||
return (@file_wrapper.instantiate_file_list(sources)).pathmap("#{@configurator.project_test_build_output_c_path}/%n#{@configurator.extension_object}")
|
||||
end
|
||||
|
||||
|
||||
def form_preprocessed_mockable_headers_filelist(mocks)
|
||||
# pathmapping note: "%{#{@configurator.cmock_mock_prefix},}n" replaces mock_prefix with nothing (signified by absence of anything after comma inside replacement brackets)
|
||||
return (@file_wrapper.instantiate_file_list(mocks)).pathmap("#{@configurator.project_test_preprocess_files_path}/%{#{@configurator.cmock_mock_prefix},}n#{@configurator.extension_header}")
|
||||
list = @file_wrapper.instantiate_file_list(mocks)
|
||||
headers = list.map do |file|
|
||||
module_name = File.basename(file).sub(/^#{@configurator.cmock_mock_prefix}/, '').sub(/\.[a-zA-Z]+$/,'')
|
||||
"#{@configurator.project_test_preprocess_files_path}/#{module_name}#{@configurator.extension_header}"
|
||||
end
|
||||
return headers
|
||||
end
|
||||
|
||||
def form_mocks_source_filelist(mocks)
|
||||
return (@file_wrapper.instantiate_file_list(mocks)).pathmap("#{@configurator.cmock_mock_path}/%n#{@configurator.extension_source}")
|
||||
list = (@file_wrapper.instantiate_file_list(mocks))
|
||||
sources = list.map{|file| "#{@configurator.cmock_mock_path}/#{file}#{@configurator.extension_source}"}
|
||||
return sources
|
||||
end
|
||||
|
||||
def form_test_dependencies_filelist(files)
|
||||
return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_test_dependencies_path}/%n#{@configurator.extension_dependencies}")
|
||||
list = @file_wrapper.instantiate_file_list(files)
|
||||
return list.pathmap("#{@configurator.project_test_dependencies_path}/%n#{@configurator.extension_dependencies}")
|
||||
end
|
||||
|
||||
def form_pass_results_filelist(path, files)
|
||||
return (@file_wrapper.instantiate_file_list(files)).pathmap("#{path}/%n#{@configurator.extension_testpass}")
|
||||
list = @file_wrapper.instantiate_file_list(files)
|
||||
return list.pathmap("#{path}/%n#{@configurator.extension_testpass}")
|
||||
end
|
||||
|
||||
end
|
@ -2,7 +2,7 @@ require 'rubygems'
|
||||
require 'rake'
|
||||
require 'set'
|
||||
require 'fileutils'
|
||||
require 'file_path_utils.rb'
|
||||
require 'ceedling/file_path_utils'
|
||||
|
||||
|
||||
class FileSystemUtils
|
@ -1,7 +1,7 @@
|
||||
require 'rubygems'
|
||||
require 'rake' # for FileList
|
||||
require 'constants'
|
||||
require 'fileutils'
|
||||
require 'ceedling/constants'
|
||||
|
||||
|
||||
class FileWrapper
|
||||
@ -29,7 +29,7 @@ class FileWrapper
|
||||
end
|
||||
|
||||
def directory_listing(glob)
|
||||
return Dir.glob(glob)
|
||||
return Dir.glob(glob, File::FNM_PATHNAME)
|
||||
end
|
||||
|
||||
def rm_f(filepath, options={})
|
||||
@ -39,7 +39,7 @@ class FileWrapper
|
||||
def rm_r(filepath, options={})
|
||||
FileUtils.rm_r(filepath, options={})
|
||||
end
|
||||
|
||||
|
||||
def cp(source, destination, options={})
|
||||
FileUtils.cp(source, destination, options)
|
||||
end
|
||||
@ -47,7 +47,7 @@ class FileWrapper
|
||||
def compare(from, to)
|
||||
return FileUtils.compare_file(from, to)
|
||||
end
|
||||
|
||||
|
||||
def open(filepath, flags)
|
||||
File.open(filepath, flags) do |file|
|
||||
yield(file)
|
||||
@ -65,7 +65,7 @@ class FileWrapper
|
||||
def write(filepath, contents, flags='w')
|
||||
File.open(filepath, flags) do |file|
|
||||
file.write(contents)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def readlines(filepath)
|
||||
@ -76,4 +76,8 @@ class FileWrapper
|
||||
return FileList.new(files)
|
||||
end
|
||||
|
||||
def mkdir(folder)
|
||||
return FileUtils.mkdir_p(folder)
|
||||
end
|
||||
|
||||
end
|
@ -1,12 +1,14 @@
|
||||
require 'rubygems'
|
||||
require 'rake' # for ext()
|
||||
require 'fileutils'
|
||||
require 'constants'
|
||||
require 'ceedling/constants'
|
||||
|
||||
|
||||
# :flags:
|
||||
# :release:
|
||||
# :compile:
|
||||
# :'test_.+'
|
||||
# - -pedantic # add '-pedantic' to every test file
|
||||
# :*: # add '-foo' to compilation of all files not main.c
|
||||
# - -foo
|
||||
# :main: # add '-Wall' to compilation of main.c
|
||||
@ -17,38 +19,56 @@ require 'constants'
|
||||
# - --bar
|
||||
# - --baz
|
||||
|
||||
def partition(hash, &predicate)
|
||||
hash.partition(&predicate).map(&:to_h)
|
||||
end
|
||||
|
||||
class Flaginator
|
||||
|
||||
constructor :configurator
|
||||
|
||||
def get_flag(hash, file_name)
|
||||
file_key = file_name.to_sym
|
||||
|
||||
# 1. try literals
|
||||
literals, magic = partition(hash) { |k, v| k.to_s =~ /^\w+$/ }
|
||||
return literals[file_key] if literals.include?(file_key)
|
||||
|
||||
any, regex = partition(magic) { |k, v| (k == :'*') || (k == :'.*') } # glob or regex wild card
|
||||
|
||||
# 2. try regexes
|
||||
find_res = regex.find { |k, v| file_name =~ /^#{k.to_s}$/ }
|
||||
return find_res[1] if find_res
|
||||
|
||||
# 3. try anything
|
||||
find_res = any.find { |k, v| file_name =~ /.*/ }
|
||||
return find_res[1] if find_res
|
||||
|
||||
# 4. well, we've tried
|
||||
return []
|
||||
end
|
||||
|
||||
def flag_down( operation, context, file )
|
||||
# create configurator accessor method
|
||||
accessor = ('flags_' + context.to_s).to_sym
|
||||
|
||||
|
||||
# create simple filename key from whatever filename provided
|
||||
file_name = File.basename( file ).ext('')
|
||||
file_key = File.basename( file ).ext('').to_sym
|
||||
|
||||
|
||||
# if no entry in configuration for flags for this context, bail out
|
||||
return [] if not @configurator.respond_to?( accessor )
|
||||
|
||||
|
||||
# get flags sub hash associated with this context
|
||||
flags = @configurator.send( accessor )
|
||||
|
||||
# if operation not represented in flags hash, bail out
|
||||
return [] if not flags.include?( operation )
|
||||
|
||||
|
||||
# redefine flags to sub hash associated with the operation
|
||||
flags = flags[operation]
|
||||
|
||||
# if our file is in the flags hash, extract the array of flags
|
||||
if (flags.include?( file_key )) then return flags[file_key]
|
||||
# if our file isn't in the flags hash, but there is default for all other files, extract array of flags
|
||||
elsif (flags.include?( :* )) then return flags[:*]
|
||||
end
|
||||
|
||||
# fall through: flags were specified but none applying to present file
|
||||
return []
|
||||
return get_flag(flags, file_name)
|
||||
end
|
||||
|
||||
end
|
@ -1,5 +1,4 @@
|
||||
require 'constants'
|
||||
|
||||
require 'ceedling/constants'
|
||||
|
||||
class Generator
|
||||
|
||||
@ -30,21 +29,22 @@ class Generator
|
||||
|
||||
def generate_dependencies_file(tool, context, source, object, dependencies)
|
||||
@streaminator.stdout_puts("Generating dependencies for #{File.basename(source)}...", Verbosity::NORMAL)
|
||||
|
||||
command =
|
||||
|
||||
command =
|
||||
@tool_executor.build_command_line(
|
||||
tool,
|
||||
[], # extra per-file command line parameters
|
||||
source,
|
||||
dependencies,
|
||||
object)
|
||||
|
||||
|
||||
@tool_executor.exec( command[:line], command[:options] )
|
||||
end
|
||||
|
||||
def generate_mock(context, header_filepath)
|
||||
arg_hash = {:header_file => header_filepath, :context => context}
|
||||
@plugin_manager.pre_mock_generate( arg_hash )
|
||||
|
||||
|
||||
begin
|
||||
@cmock_builder.cmock.setup_mocks( arg_hash[:header_file] )
|
||||
rescue
|
||||
@ -58,61 +58,73 @@ class Generator
|
||||
def generate_test_runner(context, test_filepath, runner_filepath)
|
||||
arg_hash = {:context => context, :test_file => test_filepath, :runner_file => runner_filepath}
|
||||
@plugin_manager.pre_runner_generate(arg_hash)
|
||||
|
||||
|
||||
# collect info we need
|
||||
module_name = File.basename(arg_hash[:test_file])
|
||||
test_cases = @generator_test_runner.find_test_cases( @file_finder.find_test_from_runner_path(runner_filepath) )
|
||||
mock_list = @test_includes_extractor.lookup_raw_mock_list(arg_hash[:test_file])
|
||||
|
||||
@streaminator.stdout_puts("Generating runner for #{module_name}...", Verbosity::NORMAL)
|
||||
|
||||
|
||||
test_file_includes = [] # Empty list for now, since apparently unused
|
||||
|
||||
# build runner file
|
||||
begin
|
||||
@generator_test_runner.generate(module_name, runner_filepath, test_cases, mock_list)
|
||||
@generator_test_runner.generate(module_name, runner_filepath, test_cases, mock_list, test_file_includes)
|
||||
rescue
|
||||
raise
|
||||
ensure
|
||||
@plugin_manager.post_runner_generate(arg_hash)
|
||||
@plugin_manager.post_runner_generate(arg_hash)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_object_file(tool, context, source, object, list='')
|
||||
def generate_object_file(tool, operation, context, source, object, list='', dependencies='')
|
||||
shell_result = {}
|
||||
arg_hash = {:tool => tool, :context => context, :source => source, :object => object, :list => list}
|
||||
arg_hash = {:tool => tool, :operation => operation, :context => context, :source => source, :object => object, :list => list, :dependencies => dependencies}
|
||||
@plugin_manager.pre_compile_execute(arg_hash)
|
||||
|
||||
@streaminator.stdout_puts("Compiling #{File.basename(arg_hash[:source])}...", Verbosity::NORMAL)
|
||||
command =
|
||||
@tool_executor.build_command_line( arg_hash[:tool],
|
||||
@flaginator.flag_down( operation, context, source ),
|
||||
arg_hash[:source],
|
||||
arg_hash[:object],
|
||||
arg_hash[:list],
|
||||
@flaginator.flag_down( OPERATION_COMPILE_SYM, context, source ) )
|
||||
arg_hash[:dependencies])
|
||||
|
||||
begin
|
||||
shell_result = @tool_executor.exec( command[:line], command[:options] )
|
||||
rescue ShellExecutionException => ex
|
||||
shell_result = ex.shell_result
|
||||
raise ''
|
||||
raise ex
|
||||
ensure
|
||||
arg_hash[:shell_result] = shell_result
|
||||
@plugin_manager.post_compile_execute(arg_hash)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_executable_file(tool, context, objects, executable, map='')
|
||||
def generate_executable_file(tool, context, objects, executable, map='', libraries=[])
|
||||
shell_result = {}
|
||||
arg_hash = {:tool => tool, :context => context, :objects => objects, :executable => executable, :map => map}
|
||||
arg_hash = { :tool => tool,
|
||||
:context => context,
|
||||
:objects => objects,
|
||||
:executable => executable,
|
||||
:map => map,
|
||||
:libraries => libraries
|
||||
}
|
||||
|
||||
@plugin_manager.pre_link_execute(arg_hash)
|
||||
|
||||
|
||||
@streaminator.stdout_puts("Linking #{File.basename(arg_hash[:executable])}...", Verbosity::NORMAL)
|
||||
command =
|
||||
@tool_executor.build_command_line( arg_hash[:tool],
|
||||
@flaginator.flag_down( OPERATION_LINK_SYM, context, executable ),
|
||||
arg_hash[:objects],
|
||||
arg_hash[:executable],
|
||||
arg_hash[:map],
|
||||
@flaginator.flag_down( OPERATION_LINK_SYM, context, executable ) )
|
||||
|
||||
arg_hash[:libraries]
|
||||
)
|
||||
|
||||
begin
|
||||
shell_result = @tool_executor.exec( command[:line], command[:options] )
|
||||
rescue ShellExecutionException => ex
|
||||
@ -120,13 +132,13 @@ class Generator
|
||||
"NOTICE: If the linker reports missing symbols, the following may be to blame:\n" +
|
||||
" 1. Test lacks #include statements corresponding to needed source files.\n" +
|
||||
" 2. Project search paths do not contain source files corresponding to #include statements in the test.\n"
|
||||
|
||||
|
||||
if (@configurator.project_use_mocks)
|
||||
notice += " 3. Test does not #include needed mocks.\n\n"
|
||||
else
|
||||
notice += "\n"
|
||||
end
|
||||
|
||||
|
||||
@streaminator.stderr_puts(notice, Verbosity::COMPLAIN)
|
||||
shell_result = ex.shell_result
|
||||
raise ''
|
||||
@ -139,26 +151,26 @@ class Generator
|
||||
def generate_test_results(tool, context, executable, result)
|
||||
arg_hash = {:tool => tool, :context => context, :executable => executable, :result_file => result}
|
||||
@plugin_manager.pre_test_fixture_execute(arg_hash)
|
||||
|
||||
|
||||
@streaminator.stdout_puts("Running #{File.basename(arg_hash[:executable])}...", Verbosity::NORMAL)
|
||||
|
||||
|
||||
# Unity's exit code is equivalent to the number of failed tests, so we tell @tool_executor not to fail out if there are failures
|
||||
# so that we can run all tests and collect all results
|
||||
command = @tool_executor.build_command_line(arg_hash[:tool], arg_hash[:executable])
|
||||
command = @tool_executor.build_command_line(arg_hash[:tool], [], arg_hash[:executable])
|
||||
command[:options][:boom] = false
|
||||
shell_result = @tool_executor.exec( command[:line], command[:options] )
|
||||
|
||||
shell_result[:exit_code] = 0 #Don't Let The Failure Count Make Us Believe Things Aren't Working
|
||||
@generator_helper.test_results_error_handler(executable, shell_result)
|
||||
|
||||
|
||||
processed = @generator_test_results.process_and_write_results( shell_result,
|
||||
arg_hash[:result_file],
|
||||
@file_finder.find_test_from_file_path(arg_hash[:executable]) )
|
||||
|
||||
|
||||
arg_hash[:result_file] = processed[:result_file]
|
||||
arg_hash[:results] = processed[:results]
|
||||
arg_hash[:shell_result] = shell_result # for raw output display if no plugins for formatted display
|
||||
|
||||
|
||||
@plugin_manager.post_test_fixture_execute(arg_hash)
|
||||
end
|
||||
|
||||
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
require 'constants'
|
||||
require 'ceedling/constants'
|
||||
|
||||
|
||||
class GeneratorHelper
|
@ -1,20 +1,20 @@
|
||||
require 'rubygems'
|
||||
require 'rake' # for .ext()
|
||||
require 'constants'
|
||||
require 'ceedling/constants'
|
||||
|
||||
|
||||
class GeneratorTestResults
|
||||
|
||||
constructor :configurator, :generator_test_results_sanity_checker, :yaml_wrapper
|
||||
|
||||
|
||||
def process_and_write_results(unity_shell_result, results_file, test_file)
|
||||
output_file = results_file
|
||||
|
||||
|
||||
results = get_results_structure
|
||||
|
||||
|
||||
results[:source][:path] = File.dirname(test_file)
|
||||
results[:source][:file] = File.basename(test_file)
|
||||
|
||||
results[:time] = unity_shell_result[:time] unless unity_shell_result[:time].nil?
|
||||
|
||||
# process test statistics
|
||||
if (unity_shell_result[:output] =~ TEST_STDOUT_STATISTICS_PATTERN)
|
||||
results[:counts][:total] = $1.to_i
|
||||
@ -25,11 +25,8 @@ class GeneratorTestResults
|
||||
|
||||
# remove test statistics lines
|
||||
output_string = unity_shell_result[:output].sub(TEST_STDOUT_STATISTICS_PATTERN, '')
|
||||
|
||||
# bust up the output into individual lines
|
||||
raw_unity_lines = output_string.split(/\n|\r\n/)
|
||||
|
||||
raw_unity_lines.each do |line|
|
||||
|
||||
output_string.lines do |line|
|
||||
# process unity output
|
||||
case line
|
||||
when /(:IGNORE)/
|
||||
@ -48,13 +45,13 @@ class GeneratorTestResults
|
||||
results[:stdout] << line.chomp
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@generator_test_results_sanity_checker.verify(results, unity_shell_result[:exit_code])
|
||||
|
||||
|
||||
output_file = results_file.ext(@configurator.extension_testfail) if (results[:counts][:failed] > 0)
|
||||
|
||||
|
||||
@yaml_wrapper.dump(output_file, results)
|
||||
|
||||
|
||||
return { :result_file => output_file, :result => results }
|
||||
end
|
||||
|
||||
@ -68,22 +65,25 @@ class GeneratorTestResults
|
||||
:ignores => [],
|
||||
:counts => {:total => 0, :passed => 0, :failed => 0, :ignored => 0},
|
||||
:stdout => [],
|
||||
:time => 0.0
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
def extract_line_elements(line, filename)
|
||||
# handle anything preceding filename in line as extra output to be collected
|
||||
stdout = nil
|
||||
stdout_regex = /(.+)#{Regexp.escape(filename)}.+/i
|
||||
|
||||
|
||||
if (line =~ stdout_regex)
|
||||
stdout = $1.clone
|
||||
line.sub!(/#{Regexp.escape(stdout)}/, '')
|
||||
end
|
||||
|
||||
|
||||
# collect up test results minus and extra output
|
||||
elements = (line.strip.split(':'))[1..-1]
|
||||
return {:test => elements[1], :line => elements[0].to_i, :message => (elements[3..-1].join(':')).strip}, stdout
|
||||
|
||||
return {:test => elements[1], :line => elements[0].to_i, :message => (elements[3..-1].join(':')).strip}, stdout if elements.size >= 3
|
||||
return {:test => '???', :line => -1, :message => nil} #fallback safe option. TODO better handling
|
||||
end
|
||||
|
||||
end
|
@ -1,6 +1,6 @@
|
||||
require 'constants'
|
||||
require 'rubygems'
|
||||
require 'rake' # for ext() method
|
||||
require 'ceedling/constants'
|
||||
|
||||
|
||||
class GeneratorTestResultsSanityChecker
|
||||
@ -11,8 +11,9 @@ class GeneratorTestResultsSanityChecker
|
||||
|
||||
# do no sanity checking if it's disabled
|
||||
return if (@configurator.sanity_checks == TestResultsSanityChecks::NONE)
|
||||
|
||||
ceedling_ignores_count = results[:ignores].size
|
||||
raise "results nil or empty" if results.nil? || results.empty?
|
||||
|
||||
ceedling_ignores_count = results[:ignores].size
|
||||
ceedling_failures_count = results[:failures].size
|
||||
ceedling_tests_summation = (ceedling_ignores_count + ceedling_failures_count + results[:successes].size)
|
||||
|
||||
@ -46,17 +47,19 @@ class GeneratorTestResultsSanityChecker
|
||||
private
|
||||
|
||||
def sanity_check_warning(file, message)
|
||||
notice = "\n" +
|
||||
"ERROR: Internal sanity check for test fixture '#{file.ext(@configurator.extension_executable)}' finds that #{message}\n" +
|
||||
" Possible causes:\n" +
|
||||
" 1. Your test + source dereferenced a null pointer.\n" +
|
||||
" 2. Your test + source indexed past the end of a buffer.\n" +
|
||||
" 3. Your test + source committed a memory access violation.\n" +
|
||||
" 4. Your test fixture produced an exit code of 0 despite execution ending prematurely.\n" +
|
||||
" Sanity check failures of test results are usually a symptom of interrupted test execution.\n\n"
|
||||
|
||||
@streaminator.stderr_puts( notice )
|
||||
raise
|
||||
unless defined?(CEEDLING_IGNORE_SANITY_CHECK)
|
||||
notice = "\n" +
|
||||
"ERROR: Internal sanity check for test fixture '#{file.ext(@configurator.extension_executable)}' finds that #{message}\n" +
|
||||
" Possible causes:\n" +
|
||||
" 1. Your test + source dereferenced a null pointer.\n" +
|
||||
" 2. Your test + source indexed past the end of a buffer.\n" +
|
||||
" 3. Your test + source committed a memory access violation.\n" +
|
||||
" 4. Your test fixture produced an exit code of 0 despite execution ending prematurely.\n" +
|
||||
" Sanity check failures of test results are usually a symptom of interrupted test execution.\n\n"
|
||||
|
||||
@streaminator.stderr_puts( notice )
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
end
|
52
test/vendor/ceedling/lib/ceedling/generator_test_runner.rb
vendored
Normal file
52
test/vendor/ceedling/lib/ceedling/generator_test_runner.rb
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
class GeneratorTestRunner
|
||||
|
||||
constructor :configurator, :file_path_utils, :file_wrapper
|
||||
|
||||
def find_test_cases(test_file)
|
||||
|
||||
#Pull in Unity's Test Runner Generator
|
||||
require 'generate_test_runner.rb'
|
||||
@test_runner_generator ||= UnityTestRunnerGenerator.new( @configurator.get_runner_config )
|
||||
|
||||
if (@configurator.project_use_test_preprocessor)
|
||||
|
||||
#redirect to use the preprocessor file if we're doing that sort of thing
|
||||
pre_test_file = @file_path_utils.form_preprocessed_file_filepath(test_file)
|
||||
|
||||
#actually look for the tests using Unity's test runner generator
|
||||
tests_and_line_numbers = @test_runner_generator.find_tests(@file_wrapper.read(pre_test_file))
|
||||
|
||||
#look up the line numbers in the original file
|
||||
source_lines = @file_wrapper.read(test_file).split("\n")
|
||||
source_index = 0;
|
||||
tests_and_line_numbers.size.times do |i|
|
||||
source_lines[source_index..-1].each_with_index do |line, index|
|
||||
if (line =~ /#{tests_and_line_numbers[i][:test]}/)
|
||||
source_index += index
|
||||
tests_and_line_numbers[i][:line_number] = source_index + 1
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
#Just look for the tests using Unity's test runner generator
|
||||
tests_and_line_numbers = @test_runner_generator.find_tests(@file_wrapper.read(test_file))
|
||||
end
|
||||
|
||||
return tests_and_line_numbers
|
||||
end
|
||||
|
||||
def generate(module_name, runner_filepath, test_cases, mock_list, test_file_includes=[])
|
||||
require 'generate_test_runner.rb'
|
||||
|
||||
#actually build the test runner using Unity's test runner generator
|
||||
#(there is no need to use preprocessor here because we've already looked up test cases and are passing them in here)
|
||||
@test_runner_generator ||= UnityTestRunnerGenerator.new( @configurator.get_runner_config )
|
||||
@test_runner_generator.generate( module_name,
|
||||
runner_filepath,
|
||||
test_cases,
|
||||
mock_list,
|
||||
test_file_includes)
|
||||
end
|
||||
end
|
@ -64,8 +64,8 @@ class Plugin
|
||||
def post_test_fixture_execute(arg_hash); end
|
||||
|
||||
# test task
|
||||
def pre_test; end
|
||||
def post_test; end
|
||||
def pre_test(test); end
|
||||
def post_test(test); end
|
||||
|
||||
# release task
|
||||
def pre_release; end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user