diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ecba258..74aaa0e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,31 +25,17 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - name: Checkout repository - uses: actions/checkout@v3 - - - name: Cache pip - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Cache PlatformIO - uses: actions/cache@v3 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: '3.11' + cache: 'pip' + cache-dependency-path: .github/workflows/requirements.txt - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio + - name: Install dependencies + run: pip install -r .github/workflows/requirements.txt - name: Deploy Package to Registry env: diff --git a/.github/workflows/requirements.txt b/.github/workflows/requirements.txt new file mode 100644 index 0000000..36520c3 --- /dev/null +++ b/.github/workflows/requirements.txt @@ -0,0 +1 @@ +platformio~=6.1.15 \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4bbcdfb --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,41 @@ +name: Test + +# Controls when the action will run. +on: [push, workflow_dispatch] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # The introduction just shows some useful informations. + intro: + # The type of runner that the job will run on. + runs-on: ubuntu-latest + # Steps represent a sequence of tasks that will be executed as part of the job. + steps: + - run: echo "The job was automatically triggered by a ${{ github.event_name }} event." + - run: echo "The name of the branch is ${{ github.ref }} and the repository is ${{ github.repository }}." + + test: + # The type of runner that the job will run on. + runs-on: ubuntu-latest + # Intro must run first + needs: intro + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: .github/workflows/requirements.txt + + - name: Install dependencies + run: pip install -r .github/workflows/requirements.txt + + - name: Perform static checks on native environment + run: platformio check --environment native --fail-on-defect=medium --fail-on-defect=high + + - name: Run tests on native environment + run: platformio test --environment native -vvv \ No newline at end of file diff --git a/.gitignore b/.gitignore index 259148f..081dd92 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ *.exe *.out *.app + +# Build artifacts +.pio \ No newline at end of file diff --git a/library.json b/library.json index c4df154..4b6fc46 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "ArduinoNative", - "version": "0.1.1", + "version": "0.2.0", "description": "Arduino for the native environment. It doesn't support every interface, just some of them!", "authors": [{ "name": "Andreas Merkle", diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..4466d9b --- /dev/null +++ b/platformio.ini @@ -0,0 +1,24 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:native] +platform = native @ ~1.2.1 +build_flags = + -std=c++11 + -DTARGET_NATIVE + -DUNIT_TEST +check_tool = clangtidy +check_severity = medium, high +check_src_filters = + src +check_flags = + clangtidy: --header-filter='' --checks=-*,clang-analyzer-*,performance-*,portability-*,readability-uppercase-literal-suffix,readability-redundant-control-flow --warnings-as-errors=-*,clang-analyzer-*,performance-*,portability-*,readability-uppercase-literal-suffix,readability-redundant-control-flow +check_skip_packages = yes +debug_test = test_WString diff --git a/src/Arduino.h b/src/Arduino.h index 4a2758a..932a124 100644 --- a/src/Arduino.h +++ b/src/Arduino.h @@ -1,118 +1,118 @@ -/* MIT License - * - * Copyright (c) 2023 - 2024 Andreas Merkle - * - * 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. - */ - -/******************************************************************************* - DESCRIPTION -*******************************************************************************/ -/** - * @brief Arduino native - * @author Andreas Merkle - * - * @addtogroup Arduino - * - * @{ - */ - -#ifndef ARDUINO_H -#define ARDUINO_H - -/****************************************************************************** - * Compile Switches - *****************************************************************************/ - -#define _USE_MATH_DEFINES - -/****************************************************************************** - * Includes - *****************************************************************************/ -#include -#include -#include "Serial.h" - -/****************************************************************************** - * Macros - *****************************************************************************/ - -#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) - -#ifndef PSTR -#define PSTR -#endif - -#ifndef PROGMEM -#define PROGMEM -#endif - -#define PI M_PI - -/****************************************************************************** - * Types and Classes - *****************************************************************************/ - -/** - * This type defines the prototype to get the system tick counter in ms. - */ -typedef unsigned long (*GetSystemTick)(); - -/** - * This type defines the prototype to delay for a specific time in ms. - */ -typedef void (*SystemDelay)(unsigned long ms); - -/****************************************************************************** - * Functions - *****************************************************************************/ - -/** Arduino internal specific functionality.*/ -namespace Arduino -{ - /** - * Setup the Arduino library and call the application setup() function. - */ - void setup(GetSystemTick getSystemTickFunc, SystemDelay systemDelayFunc); - - /** - * Loop shall be called periodically and handles Arduino library - * functionality and call the application loop() function. - */ - void loop(); -} - -/** - * Returns the number of milliseconds passed since the system start. - * - * @return The number of milliseconds. - */ -extern unsigned long millis(); - -/** - * Delays the program for the specified amount of milliseconds. In the mean time the - * simulation still steps to prevent an endless loop. - * - * @param[in] ms The amount of milliseconds that the program should be delayed by. - */ -extern void delay(unsigned long ms); - -#endif /* ARDUINO_H */ - +/* MIT License + * + * Copyright (c) 2023 - 2024 Andreas Merkle + * + * 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. + */ + +/******************************************************************************* + DESCRIPTION +*******************************************************************************/ +/** + * @brief Arduino native + * @author Andreas Merkle + * + * @addtogroup Arduino + * + * @{ + */ + +#ifndef ARDUINO_H +#define ARDUINO_H + +/****************************************************************************** + * Compile Switches + *****************************************************************************/ + +#define _USE_MATH_DEFINES + +/****************************************************************************** + * Includes + *****************************************************************************/ +#include +#include +#include "Serial.h" + +/****************************************************************************** + * Macros + *****************************************************************************/ + +#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) + +#ifndef PSTR +#define PSTR +#endif + +#ifndef PROGMEM +#define PROGMEM +#endif + +#define PI M_PI + +/****************************************************************************** + * Types and Classes + *****************************************************************************/ + +/** + * This type defines the prototype to get the system tick counter in ms. + */ +typedef unsigned long (*GetSystemTick)(); + +/** + * This type defines the prototype to delay for a specific time in ms. + */ +typedef void (*SystemDelay)(unsigned long ms); + +/****************************************************************************** + * Functions + *****************************************************************************/ + +/** Arduino internal specific functionality.*/ +namespace Arduino +{ + /** + * Setup the Arduino library and call the application setup() function. + */ + void setup(GetSystemTick getSystemTickFunc, SystemDelay systemDelayFunc); + + /** + * Loop shall be called periodically and handles Arduino library + * functionality and call the application loop() function. + */ + void loop(); +} + +/** + * Returns the number of milliseconds passed since the system start. + * + * @return The number of milliseconds. + */ +extern unsigned long millis(); + +/** + * Delays the program for the specified amount of milliseconds. In the mean time the + * simulation still steps to prevent an endless loop. + * + * @param[in] ms The amount of milliseconds that the program should be delayed by. + */ +extern void delay(unsigned long ms); + +#endif /* ARDUINO_H */ + /** @} */ \ No newline at end of file diff --git a/src/WString.h b/src/WString.h new file mode 100644 index 0000000..6253b76 --- /dev/null +++ b/src/WString.h @@ -0,0 +1,441 @@ +/* MIT License + * + * Copyright (c) 2023 - 2024 Andreas Merkle + * + * 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. + */ + +/******************************************************************************* + DESCRIPTION +*******************************************************************************/ +/** + * @brief String implementation for test + * @author Andreas Merkle + * @author Luca Dubies + * @author Gabryel Reyes + * + * @addtogroup Arduino + * + * @{ + */ + +#ifndef WSTRING_H +#define WSTRING_H + +/****************************************************************************** + * Compile Switches + *****************************************************************************/ + +#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 +#endif + +/****************************************************************************** + * Includes + *****************************************************************************/ +#include +#include +#include +#include + +/****************************************************************************** + * Macros + *****************************************************************************/ + +/****************************************************************************** + * Types and Classes + *****************************************************************************/ + +/** + * String class for test purposes only. + */ +class String +{ +public: + /** + * Constructs a string. + */ + String() : m_stdStr() + { + } + + /** + * Destroys a string. + */ + ~String() + { + } + + /** + * Constructs a string by copying another. + * + * @param[in] other String to copy + */ + String(const String& other) : m_stdStr(other.m_stdStr) + { + } + + /** + * Constructs a string by copying another. + * + * @param[in] other String to copy + */ + String(const char* other) : m_stdStr(other) + { + } + + /** + * Constructs a string by copying another. + * + * @param[in] other String to copy + */ + String(const std::string& other) : m_stdStr(other) + { + } + + /** + * Constructs a string by copying a number of characters from another. + * + * @param[in] other String to copy + * @param[in] length Number of characters to copy + */ + String(const char* other, unsigned int length) : m_stdStr(other, length) + { + } + + /** + * Constructs a string by copying a single character. + * + * @param[in] c Single character + */ + String(char c) : m_stdStr(1, c) + { + } + + /** + * Assign a string. + * + * @param[in] other String, which to assign. + * + * @return String + */ + String& operator=(const String& other) + { + if (this != &other) + { + m_stdStr = other.m_stdStr; + } + + return *this; + } + + /** + * Compare two strings. + * + * @param[in] other String, which to compare with. + * + * @return If the strings are equal, it will return true otherwise false. + */ + bool operator==(const String& other) const + { + return m_stdStr == other.m_stdStr; + } + + /** + * Compare two strings. + * + * @param[in] other String, which to compare with. + * + * @return If the strings are equal, it will return true otherwise false. + */ + bool operator!=(const String& other) const + { + return m_stdStr != other.m_stdStr; + } + + /** + * Get character at given index. + * If the index is out of bounds, it will return '\0'. + * + * @param[in] index Character index in the string. + * + * @return Character + */ + char operator[](unsigned int index) const + { + char singleChar = '\0'; + + if (m_stdStr.length() > index) + { + singleChar = m_stdStr[index]; + } + + return singleChar; + } + + String& operator+=(const String& other) + { + m_stdStr += other.m_stdStr; + + return *this; + } + + String& operator+=(char c) + { + m_stdStr += c; + + return *this; + } + + String& operator+=(int number) + { + m_stdStr += std::to_string(number); + + return *this; + } + + String operator+(const String& other) const + { + String tmp = *this; + tmp += other; + + return tmp; + } + + /** + * Get string as char array. + * + * @return Char array + */ + const char* c_str() const + { + return m_stdStr.c_str(); + } + + /** + * Get string length. + * + * @return String length + */ + size_t length() const + { + return m_stdStr.length(); + } + + /** + * Return the substring from index to the end. + * + * @param[in] index Index. + * + * @return Substring + */ + String substring(unsigned int index) const + { + return m_stdStr.substr(index); + } + + /** + * Return the substring between left and right index. + * + * @param[in] left Index left + * @param[in] right Index right + * + * @return Substring + */ + String substring(unsigned int left, unsigned int right) const + { + return m_stdStr.substr(left, right - left); + } + + /** + * Starts string with given pattern? + * + * @param[in] s2 Pattern + * + * @return If string starts with pattern, it will return true otherwise false. + */ + unsigned char startsWith(const String& s2) const + { + if (length() < s2.length()) + { + return 0U; + } + + return startsWith(s2, 0); + } + + /** + * Starts string with given pattern from offset? + * + * @param[in] s2 Pattern + * @param[in] offset Offset + * + * @return If string starts with pattern, it will return true otherwise false. + */ + unsigned char startsWith(const String& s2, unsigned int offset) const + { + return 0 == m_stdStr.rfind(s2.c_str(), 0); + } + + /** + * Clear string. + */ + void clear() + { + m_stdStr.clear(); + } + + /** + * Is string empty? + * + * @return If empty, it will return true otherwise false. + */ + bool isEmpty() const + { + return m_stdStr.empty(); + } + + /** + * Get last index of given string. + * + * @param[in] other String to search for. + * + * @return If found, it will return the index otherwise -1. + */ + int lastIndexOf(const String& other) const + { + int index = -1; + size_t pos = m_stdStr.find_last_of(other.m_stdStr); + + if (std::string::npos != pos) + { + index = pos; + } + + return index; + } + + /** + * Compare string case insenstive. + * + * @param[in] other String to compare with. + * + * @return If equal, it will return true otherwise false. + */ + bool equalsIgnoreCase(const String& other) const + { + return (m_stdStr.length() == other.m_stdStr.length()) && + (std::equal(m_stdStr.begin(), m_stdStr.end(), other.m_stdStr.begin(), icharEquals)); + } + + /** + * Concatenate a string. + * + * @param[in] other String to concatenate. + * + * @return If successful, it will return true otherwise false. + */ + bool concat(const String& other) + { + (void)this->operator+=(other); + return true; + } + + /** + * Concatenate a string. + * + * @param[in] other String to concatenate. + * + * @return If successful, it will return true otherwise false. + */ + bool concat(const char* other) + { + if (nullptr == other) + { + return false; + } + + (void)this->operator+=(other); + return true; + } + + /** + * Concatenate a character. + * + * @param[in] c Character to concatenate. + * + * @return If successful, it will return true otherwise false. + */ + bool concat(char c) + { + (void)this->operator+=(c); + return true; + } + + /** + * If the find pattern is found in the String, it is replaced by the String replace. + * + * @param[in] find Pattern + * @param[in] replace Replacement for Pattern + */ + void replace(const String& find, const String& replace) + { + size_t replaceLen = replace.length(); + size_t findLen = find.length(); + size_t foundIdx = 0U; + + if ((0 == length()) || (0 == findLen)) + { + /* return early if this or find pattern have length 0 */ + return; + } + + foundIdx = m_stdStr.find(find.m_stdStr); + + while (std::string::npos != foundIdx) + { + m_stdStr.replace(foundIdx, findLen, replace.m_stdStr); + foundIdx = m_stdStr.find(find.m_stdStr); + } + } + +private: + std::string m_stdStr; /**< Internal used std string. */ + + /** + * Compare single characters case insensitive. + * + * @param[in] ch1 Character 1 + * @param[in] ch2 Character 2 + * + * @return If equal, it will return true otherwise false. + */ + static bool icharEquals(unsigned char ch1, unsigned char ch2) + { + return std::tolower(ch1) == std::tolower(ch2); + } +}; + +/****************************************************************************** + * Functions + *****************************************************************************/ + +#endif /* WSTRING_H */ + +/** @} */ diff --git a/test/test_Wstring/test_WString.cpp b/test/test_Wstring/test_WString.cpp new file mode 100644 index 0000000..811a53a --- /dev/null +++ b/test/test_Wstring/test_WString.cpp @@ -0,0 +1,197 @@ +/* MIT License + * + * Copyright (c) 2023 - 2024 Andreas Merkle + * + * 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. + */ + +/******************************************************************************* + DESCRIPTION +*******************************************************************************/ +/** + * @brief Test (some) WString functions. + * @author Luca Dubies + */ + +/****************************************************************************** + * Includes + *****************************************************************************/ +#include +#include + +/****************************************************************************** + * Compiler Switches + *****************************************************************************/ + +/****************************************************************************** + * Macros + *****************************************************************************/ + +/****************************************************************************** + * Types and classes + *****************************************************************************/ + +/****************************************************************************** + * Prototypes + *****************************************************************************/ + +static void testWStringReplacement(void); +static void testWStringAppend(void); +static void testWStringConcat(void); + +/****************************************************************************** + * Local Variables + *****************************************************************************/ + +/****************************************************************************** + * Public Methods + *****************************************************************************/ + +/****************************************************************************** + * Protected Methods + *****************************************************************************/ + +/****************************************************************************** + * Private Methods + *****************************************************************************/ + +/****************************************************************************** + * External Functions + *****************************************************************************/ + +/** + * Main entry point + * + * @param[in] argc Number of command line arguments + * @param[in] argv Command line arguments + * + * @returns Test failure count + */ +extern int main(int argc, char** argv) +{ + UNITY_BEGIN(); + + RUN_TEST(testWStringReplacement); + RUN_TEST(testWStringAppend); + RUN_TEST(testWStringConcat); + + return UNITY_END(); +} + +/** + * Setup a test. This function will be called before every test by unity. + */ +extern void setUp(void) +{ + /* Not used. */ +} + +/** + * Clean up test. This function will be called after every test by unity. + */ +extern void tearDown(void) +{ + /* Not used. */ +} + +/****************************************************************************** + * Local Functions + *****************************************************************************/ + +/** + * Test WString replace. + */ +static void testWStringReplacement(void) +{ + String original = String("I am a long string that will feature some replacements!"); + String tester; + + /* same length replacement */ + tester = String(original); + tester.replace(String("long"), String("same")); + TEST_ASSERT_EQUAL_STRING("I am a same string that will feature some replacements!", tester.c_str()); + + /* shorter replacement */ + tester = String(original); + tester.replace(String("feature"), String("have")); + TEST_ASSERT_EQUAL_STRING("I am a long string that will have some replacements!", tester.c_str()); + TEST_ASSERT_EQUAL(original.length() - 3, tester.length()); + + /* longer replacement */ + tester = String(original); + tester.replace("a", "AAA"); + TEST_ASSERT_EQUAL_STRING("I AAAm AAA long string thAAAt will feAAAture some replAAAcements!", tester.c_str()); + TEST_ASSERT_EQUAL(original.length() + 10, tester.length()); + + /* replacement with empty string */ + tester = String(original); + tester.replace(" ", ""); + TEST_ASSERT_EQUAL_STRING("Iamalongstringthatwillfeaturesomereplacements!", tester.c_str()); + + /* test original empty string */ + tester = String(""); + tester.replace("never", "happens"); + TEST_ASSERT_EQUAL_STRING("", tester.c_str()); +} + +/** + * Test WString += implementation. + */ +static void testWStringAppend(void) +{ + String original = String("Im short"); + char classic[4]; + classic[0] = 'H'; + classic[1] = 'I'; + classic[2] = '!'; + classic[3] = '\0'; + + original += String("- first amendment -"); + original += classic; + original += '-'; + + TEST_ASSERT_EQUAL_STRING("Im short- first amendment -HI!-", original.c_str()); + + String tester = String(""); + tester += "Im new"; + TEST_ASSERT_EQUAL_STRING("Im new", tester.c_str()); +} + +/** + * Test WString += implementation. + */ +static void testWStringConcat(void) +{ + String original = String("Im short"); + char classic[4]; + classic[0] = 'H'; + classic[1] = 'I'; + classic[2] = '!'; + classic[3] = '\0'; + + original.concat(String("- first amendment -")); + original.concat(classic); + original.concat('-'); + + TEST_ASSERT_EQUAL_STRING("Im short- first amendment -HI!-", original.c_str()); + + String tester = String(""); + tester.concat("Im new"); + TEST_ASSERT_EQUAL_STRING("Im new", tester.c_str()); +}