/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 KiCad Developers, see CHANGELOG.TXT for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /** * @file test_coroutine.cpp * Test suite for coroutines. * * See also the coroutine utility in qa/common_tools for a command line * test utility. */ #include #include #include /** * An event in a simple coroutine harness. */ struct COROUTINE_TEST_EVENT { enum class TYPE { START, CALL, YIELD, RETURNED, END, }; TYPE m_type; int m_value; bool operator==( const COROUTINE_TEST_EVENT& aOther ) const { return m_type == aOther.m_type && m_value == aOther.m_value; } bool operator!=( const COROUTINE_TEST_EVENT& aOther ) const { return !operator==( aOther ); } }; /** * Define a stream function for logging this type. * * TODO: convert to boost_test_print_type when Boost minver > 1.64 */ std::ostream& operator<<( std::ostream& os, const COROUTINE_TEST_EVENT& aObj ) { os << "COROUTINE_TEST_EVENT[ type: " << (int) aObj.m_type << ", value: " << aObj.m_value << " ]"; return os; } /** * Simple coroutine harness that runs a coroutine that increments a number up * to a pre-set limit, spitting out coroutine events as it goes. * * This can then be used to ensure the events are occurring as expected. */ class COROUTINE_INCREMENTING_HARNESS { public: /** * The coroutine test take ints and returns them */ using TEST_COROUTINE = COROUTINE; using EVT_HANDLER = std::function; COROUTINE_INCREMENTING_HARNESS( EVT_HANDLER aHandler, int aCount ) : m_handler( aHandler ), m_count( aCount ) { } int CountTo( int n ) { m_handler( { COROUTINE_TEST_EVENT::TYPE::START, 0 } ); for( int i = 1; i <= n; i++ ) { m_handler( { COROUTINE_TEST_EVENT::TYPE::YIELD, i } ); m_cofunc->KiYield( i ); } return 0; } void Run() { m_cofunc = std::make_unique( this, &COROUTINE_INCREMENTING_HARNESS::CountTo ); m_handler( { COROUTINE_TEST_EVENT::TYPE::CALL, m_count } ); m_cofunc->Call( m_count ); int ret_val = 0; while( m_cofunc->Running() ) { ret_val = m_cofunc->ReturnValue(); m_handler( { COROUTINE_TEST_EVENT::TYPE::RETURNED, ret_val } ); m_cofunc->Resume(); } m_handler( { COROUTINE_TEST_EVENT::TYPE::END, ret_val } ); } EVT_HANDLER m_handler; std::unique_ptr m_cofunc; int m_count; }; /** * Declare the test suite */ BOOST_AUTO_TEST_SUITE( Coroutine ) /** * A basic test to repeatedly call a coroutine and check that it yields * values as expected. */ BOOST_AUTO_TEST_CASE( Increment ) { const int count = 2; const std::vector exp_events = { { COROUTINE_TEST_EVENT::TYPE::CALL, count }, { COROUTINE_TEST_EVENT::TYPE::START, 0 }, { COROUTINE_TEST_EVENT::TYPE::YIELD, 1 }, { COROUTINE_TEST_EVENT::TYPE::RETURNED, 1 }, { COROUTINE_TEST_EVENT::TYPE::YIELD, 2 }, { COROUTINE_TEST_EVENT::TYPE::RETURNED, 2 }, { COROUTINE_TEST_EVENT::TYPE::END, 2 }, }; std::vector received_events; auto handler = [&]( const COROUTINE_TEST_EVENT& aEvent ) { received_events.push_back( aEvent ); }; COROUTINE_INCREMENTING_HARNESS harness( handler, count ); harness.Run(); BOOST_CHECK_EQUAL_COLLECTIONS( received_events.begin(), received_events.end(), exp_events.begin(), exp_events.end() ); } BOOST_AUTO_TEST_SUITE_END()