QA eeschema: add some tests

This adds a few tests on:

* LIB_PART
* SCH_PIN
* SCH_SHEET
* SCH_SHEET_PATH

These tests exercise some of the basic code paths in these classes
and show some of the expected behaviours.

None of these tests are particularly ground-breaking, but they
provide a starting point to build out further tests, and to ensure
the already-covered behaviour is stable.

It does expose some places where SCH_SHEET could probably use const.
This commit is contained in:
John Beard 2019-04-23 09:18:15 +01:00
parent 0617bffce0
commit 840e08fa78
16 changed files with 1406 additions and 4 deletions

View File

@ -130,6 +130,15 @@ the triggering of a bug prior to fixing it. This is advantageous, not only from
a "project history" perspective, but also to ensure that the test you write to a "project history" perspective, but also to ensure that the test you write to
catch the bug in question does, in fact, catch the bug in the first place. catch the bug in question does, in fact, catch the bug in the first place.
### Assertions {#test-assertions}
It is possible to check for assertions in unit tests. When running the unit
tests, `wxASSERT` calls are caught and re-thrown as exceptions. You can then use
the `CHECK_WX_ASSERT` macro to check this is called in Debug builds. In Release
builds, the check is not run, as `wxASSERT` is disabled in these builds.
You can use this to ensure that code rejects invalid input correctly.
## Python modules {#python-tests} ## Python modules {#python-tests}
The Pcbnew Python modules have some test programs in the `qa` directory. The Pcbnew Python modules have some test programs in the `qa` directory.

View File

@ -32,12 +32,20 @@ add_executable( qa_eeschema
../../common/colors.cpp ../../common/colors.cpp
../../common/observable.cpp ../../common/observable.cpp
# need the mock Pgm for many functions
mocks_eeschema.cpp
eeschema_test_utils.cpp eeschema_test_utils.cpp
timestamp_test_utils.cpp
# The main test entry points # The main test entry points
test_module.cpp test_module.cpp
test_eagle_plugin.cpp test_eagle_plugin.cpp
test_lib_part.cpp
test_sch_pin.cpp
test_sch_sheet.cpp
test_sch_sheet_path.cpp
# Older CMakes cannot link OBJECT libraries # Older CMakes cannot link OBJECT libraries
# https://cmake.org/pipermail/cmake/2013-November/056263.html # https://cmake.org/pipermail/cmake/2013-November/056263.html

View File

@ -0,0 +1,122 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 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 utils (e.g. print helpers and test predicates for LIB_FIELD objects
*/
#ifndef QA_EESCHEMA_LIB_FIELD_TEST_UTILS__H
#define QA_EESCHEMA_LIB_FIELD_TEST_UTILS__H
#include <unit_test_utils/unit_test_utils.h>
#include <class_libentry.h>
#include <template_fieldnames.h>
BOOST_TEST_PRINT_NAMESPACE_OPEN
{
template <>
struct print_log_value<LIB_FIELD>
{
inline void operator()( std::ostream& os, LIB_FIELD const& f )
{
os << "LIB_FIELD[ " << f.GetName() << " ]";
}
};
template <>
struct print_log_value<LIB_FIELDS>
{
inline void operator()( std::ostream& os, LIB_FIELDS const& f )
{
os << "LIB_FIELDS[ " << f.size() << " ]";
}
};
}
BOOST_TEST_PRINT_NAMESPACE_CLOSE
namespace KI_TEST
{
/**
* Predicate to check a field name is as expected
* @param aField LIB_FIELD to check the name
* @param aExpectedName the expected field name
* @param aExpectedId the expected field id
* @return true if match
*/
bool FieldNameIdMatches(
const LIB_FIELD& aField, const std::string& aExpectedName, int aExpectedId )
{
bool ok = true;
const auto gotName = aField.GetName( false );
if( gotName != aExpectedName )
{
BOOST_TEST_INFO(
"Field name mismatch: got '" << gotName << "', expected '" << aExpectedName );
ok = false;
}
const int gotId = aField.GetId();
if( gotId != aExpectedId )
{
BOOST_TEST_INFO( "Field ID mismatch: got '" << gotId << "', expected '" << aExpectedId );
ok = false;
}
return ok;
}
/**
* Predicate to check that the mandatory fields in a LIB_FIELDS object look sensible
* @param aFields the fields to check
* @return true if valid
*/
bool AreDefaultFieldsCorrect( const LIB_FIELDS& aFields )
{
const unsigned expectedCount = NumFieldType::MANDATORY_FIELDS;
if( aFields.size() < expectedCount )
{
BOOST_TEST_INFO(
"Expected at least " << expectedCount << " fields, got " << aFields.size() );
return false;
}
bool ok = true;
ok &= FieldNameIdMatches( aFields[0], "Reference", NumFieldType::REFERENCE );
ok &= FieldNameIdMatches( aFields[1], "Value", NumFieldType::VALUE );
ok &= FieldNameIdMatches( aFields[2], "Footprint", NumFieldType::FOOTPRINT );
ok &= FieldNameIdMatches( aFields[3], "Datasheet", NumFieldType::DATASHEET );
return ok;
}
} // namespace KI_TEST
#endif // QA_EESCHEMA_LIB_FIELD_TEST_UTILS__H

View File

@ -0,0 +1,141 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 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
*/
#include <kiface_i.h>
#include <pgm_base.h>
#include <sch_edit_frame.h>
// The main sheet of the project
SCH_SHEET* g_RootSheet = nullptr;
// a transform matrix, to display components in lib editor
TRANSFORM DefaultTransform = TRANSFORM( 1, 0, 0, -1 );
static struct IFACE : public KIFACE_I
{
// Of course all are overloads, implementations of the KIFACE.
IFACE( const char* aName, KIWAY::FACE_T aType ) : KIFACE_I( aName, aType )
{
}
bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override
{
return true;
}
void OnKifaceEnd() override
{
}
wxWindow* CreateWindow(
wxWindow* aParent, int aClassId, KIWAY* aKiway, int aCtlBits = 0 ) override
{
assert( false );
return nullptr;
}
/**
* Function IfaceOrAddress
* return a pointer to the requested object. The safest way to use this
* is to retrieve a pointer to a static instance of an interface, similar to
* how the KIFACE interface is exported. But if you know what you are doing
* use it to retrieve anything you want.
*
* @param aDataId identifies which object you want the address of.
*
* @return void* - and must be cast into the know type.
*/
void* IfaceOrAddress( int aDataId ) override
{
return NULL;
}
} kiface( "mock_eeschema", KIWAY::FACE_SCH );
static struct PGM_MOCK_EESCHEMA_FRAME : public PGM_BASE
{
bool OnPgmInit();
void OnPgmExit()
{
Kiway.OnKiwayEnd();
// Destroy everything in PGM_BASE, especially wxSingleInstanceCheckerImpl
// earlier than wxApp and earlier than static destruction would.
PGM_BASE::Destroy();
}
void MacOpenFile( const wxString& aFileName ) override
{
wxFileName filename( aFileName );
if( filename.FileExists() )
{
#if 0
// this pulls in EDA_DRAW_FRAME type info, which we don't want in
// the single_top link image.
KIWAY_PLAYER* frame = dynamic_cast<KIWAY_PLAYER*>( App().GetTopWindow() );
#else
KIWAY_PLAYER* frame = (KIWAY_PLAYER*) App().GetTopWindow();
#endif
if( frame )
frame->OpenProjectFiles( std::vector<wxString>( 1, aFileName ) );
}
}
} program;
PGM_BASE& Pgm()
{
return program;
}
KIFACE_I& Kiface()
{
return kiface;
}
static COLOR4D s_layerColor[LAYER_ID_COUNT];
COLOR4D GetLayerColor( SCH_LAYER_ID aLayer )
{
unsigned layer = ( aLayer );
wxASSERT( layer < arrayDim( s_layerColor ) );
return s_layerColor[layer];
}
void SetLayerColor( COLOR4D aColor, SCH_LAYER_ID aLayer )
{
// Do not allow non-background layers to be completely white.
// This ensures the BW printing recognizes that the colors should be
// printed black.
if( aColor == COLOR4D::WHITE && aLayer != LAYER_SCHEMATIC_BACKGROUND )
aColor.Darken( 0.01 );
unsigned layer = aLayer;
wxASSERT( layer < arrayDim( s_layerColor ) );
s_layerColor[layer] = aColor;
}

View File

@ -0,0 +1,206 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 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 suite for LIB_PART
*/
#include <unit_test_utils/unit_test_utils.h>
// Code under test
#include <class_libentry.h>
#include "lib_field_test_utils.h"
class TEST_LIB_PART_FIXTURE
{
public:
TEST_LIB_PART_FIXTURE() : m_part_no_data( "part_name", nullptr )
{
}
///> Part with no extra data set
LIB_PART m_part_no_data;
};
/**
* Declare the test suite
*/
BOOST_FIXTURE_TEST_SUITE( LibPart, TEST_LIB_PART_FIXTURE )
/**
* Check that we can get the basic properties out as expected
*/
BOOST_AUTO_TEST_CASE( DefaultProperties )
{
BOOST_CHECK_EQUAL( m_part_no_data.GetName(), "part_name" );
// Didn't set a library, so this is empty
BOOST_CHECK_EQUAL( m_part_no_data.GetLibraryName(), "" );
BOOST_CHECK_EQUAL( m_part_no_data.GetLib(), nullptr );
// only get the root
BOOST_CHECK_EQUAL( m_part_no_data.GetAliasCount(), 1 );
// no sub units
BOOST_CHECK_EQUAL( m_part_no_data.GetUnitCount(), 1 );
BOOST_CHECK_EQUAL( m_part_no_data.IsMulti(), false );
// no conversion
BOOST_CHECK_EQUAL( m_part_no_data.HasConversion(), false );
}
/**
* Check the drawings on a "blank" LIB_PART
*/
BOOST_AUTO_TEST_CASE( DefaultDrawings )
{
// default drawings exist
BOOST_CHECK_EQUAL( m_part_no_data.GetDrawItems().size(), 4 );
}
/**
* Check the default fields are present as expected
*/
BOOST_AUTO_TEST_CASE( DefaultFields )
{
LIB_FIELDS fields;
m_part_no_data.GetFields( fields );
// Should get the 4 default fields
BOOST_CHECK_PREDICATE( KI_TEST::AreDefaultFieldsCorrect, ( fields ) );
// but no more (we didn't set them)
BOOST_CHECK_EQUAL( fields.size(), NumFieldType::MANDATORY_FIELDS );
// also check the default field accessors
BOOST_CHECK_PREDICATE( KI_TEST::FieldNameIdMatches,
( m_part_no_data.GetReferenceField() )( "Reference" )( NumFieldType::REFERENCE ) );
BOOST_CHECK_PREDICATE( KI_TEST::FieldNameIdMatches,
( m_part_no_data.GetValueField() )( "Value" )( NumFieldType::VALUE ) );
BOOST_CHECK_PREDICATE( KI_TEST::FieldNameIdMatches,
( m_part_no_data.GetFootprintField() )( "Footprint" )( NumFieldType::FOOTPRINT ) );
}
/**
* Test adding fields to a LIB_PART
*/
BOOST_AUTO_TEST_CASE( AddedFields )
{
LIB_FIELDS fields;
m_part_no_data.GetFields( fields );
// Ctor takes non-const ref (?!)
const std::string newFieldName = "new_field";
wxString nonConstNewFieldName = newFieldName;
fields.push_back( LIB_FIELD( 42, nonConstNewFieldName ) );
// fairly roundabout way to add a field, but it is what it is
m_part_no_data.SetFields( fields );
// Should get the 4 default fields
BOOST_CHECK_PREDICATE( KI_TEST::AreDefaultFieldsCorrect, ( fields ) );
// and our new one
BOOST_REQUIRE_EQUAL( fields.size(), NumFieldType::MANDATORY_FIELDS + 1 );
BOOST_CHECK_PREDICATE( KI_TEST::FieldNameIdMatches,
( fields[NumFieldType::MANDATORY_FIELDS] )( newFieldName )( 42 ) );
// Check by-id lookup
LIB_FIELD* gotNewField = m_part_no_data.GetField( 42 );
BOOST_REQUIRE_NE( gotNewField, nullptr );
BOOST_CHECK_PREDICATE( KI_TEST::FieldNameIdMatches, ( *gotNewField )( newFieldName )( 42 ) );
// Check by-name lookup
gotNewField = m_part_no_data.FindField( newFieldName );
BOOST_REQUIRE_NE( gotNewField, nullptr );
BOOST_CHECK_PREDICATE( KI_TEST::FieldNameIdMatches, ( *gotNewField )( newFieldName )( 42 ) );
}
struct TEST_LIB_PART_SUBREF_CASE
{
int m_index;
bool m_addSep;
std::string m_expSubRef;
};
/**
* Test the subreference indexing
*/
BOOST_AUTO_TEST_CASE( SubReference )
{
const std::vector<TEST_LIB_PART_SUBREF_CASE> cases = {
{
1,
false,
"A",
},
{
2,
false,
"B",
},
{
26,
false,
"Z",
},
{
27,
false,
"AA",
},
{ // haven't configured a separator, so should be nothing
1,
true,
"A",
},
};
for( const auto& c : cases )
{
BOOST_TEST_CONTEXT(
"Subref: " << c.m_index << ", " << c.m_addSep << " -> '" << c.m_expSubRef << "'" )
{
const auto subref = m_part_no_data.SubReference( c.m_index, c.m_addSep );
BOOST_CHECK_EQUAL( subref, c.m_expSubRef );
}
}
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -30,11 +30,29 @@
#include <wx/init.h> #include <wx/init.h>
#include <unit_test_utils/wx_assert.h>
/*
* Simple function to handle a WX assertion and throw a real exception.
*
* This is useful when you want to check assertions fire in unit tests.
*/
void wxAssertThrower( const wxString& aFile, int aLine, const wxString& aFunc,
const wxString& aCond, const wxString& aMsg )
{
throw KI_TEST::WX_ASSERT_ERROR( aFile, aLine, aFunc, aCond, aMsg );
}
bool init_unit_test() bool init_unit_test()
{ {
boost::unit_test::framework::master_test_suite().p_name.value = "Common Eeschema module tests"; boost::unit_test::framework::master_test_suite().p_name.value = "Common Eeschema module tests";
return wxInitialize();
bool ok = wxInitialize();
wxSetAssertHandler( &wxAssertThrower );
return ok;
} }

View File

@ -0,0 +1,159 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 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 suite for SCH_PIM
*/
#include <unit_test_utils/geometry.h>
#include <unit_test_utils/unit_test_utils.h>
#include <unit_test_utils/wx_assert.h>
// Code under test
#include <sch_pin.h>
#include <sch_component.h>
#include <eda_rect.h>
class TEST_SCH_PIN_FIXTURE
{
public:
TEST_SCH_PIN_FIXTURE()
: m_parent_part( "parent_part", nullptr ),
m_lib_pin( &m_parent_part ),
m_parent_comp( wxPoint( 0, 0 ), nullptr ),
m_sch_pin( &m_lib_pin, &m_parent_comp )
{
// give the pin some kind of data we can use to test
m_lib_pin.SetNumber( "42" );
m_lib_pin.SetName( "pinname" );
m_lib_pin.SetType( ELECTRICAL_PINTYPE::PIN_INPUT );
SCH_SHEET_PATH path;
m_parent_comp.SetRef( &path, "U2" );
}
LIB_PART m_parent_part;
LIB_PIN m_lib_pin;
SCH_COMPONENT m_parent_comp;
SCH_PIN m_sch_pin;
};
/**
* Declare the test suite
*/
BOOST_FIXTURE_TEST_SUITE( SchPin, TEST_SCH_PIN_FIXTURE )
/**
* Check basic properties of an un-modified SCH_PIN object
*/
BOOST_AUTO_TEST_CASE( DefaultProperties )
{
BOOST_CHECK_EQUAL( m_sch_pin.GetParentComponent(), &m_parent_comp );
BOOST_CHECK_EQUAL( m_sch_pin.GetLibPin(), &m_lib_pin );
BOOST_CHECK_EQUAL( m_sch_pin.GetPosition(), wxPoint( 0, 0 ) );
// These just forward to LIB_PIN for now, so this isn't very interesting
// but later we will want to test these functions for SCH_PIN's own functionality
BOOST_CHECK_EQUAL( m_sch_pin.IsVisible(), m_lib_pin.IsVisible() );
BOOST_CHECK_EQUAL( m_sch_pin.GetName(), m_lib_pin.GetName() );
BOOST_CHECK_EQUAL( m_sch_pin.GetNumber(), m_lib_pin.GetNumber() );
BOOST_CHECK_EQUAL( m_sch_pin.GetType(), m_lib_pin.GetType() );
BOOST_CHECK_EQUAL( m_sch_pin.IsPowerConnection(), m_lib_pin.IsPowerConnection() );
}
/**
* Check the assignment operator
*/
BOOST_AUTO_TEST_CASE( Assign )
{
SCH_PIN assigned = m_sch_pin;
BOOST_CHECK_EQUAL( assigned.GetParentComponent(), &m_parent_comp );
}
/**
* Check the copy ctor
*/
BOOST_AUTO_TEST_CASE( Copy )
{
SCH_PIN copied( m_sch_pin );
BOOST_CHECK_EQUAL( copied.GetParentComponent(), &m_parent_comp );
}
/**
* Check the pin dangling flag
*/
BOOST_AUTO_TEST_CASE( PinDangling )
{
// dangles by default
BOOST_CHECK_EQUAL( m_sch_pin.IsDangling(), true );
// all you have to do to un-dangle is say so
m_sch_pin.SetIsDangling( false );
BOOST_CHECK_EQUAL( m_sch_pin.IsDangling(), false );
// and the same to re-dangle
m_sch_pin.SetIsDangling( true );
BOOST_CHECK_EQUAL( m_sch_pin.IsDangling(), true );
}
/**
* Check the pin labelling
*/
BOOST_AUTO_TEST_CASE( PinNumbering )
{
SCH_SHEET_PATH path;
const wxString name = m_sch_pin.GetDefaultNetName( path );
BOOST_CHECK_EQUAL( name, "Net-(U2-Pad42)" );
// do it again: this should now (transparently) go though the net name map
// can't really check directly, but coverage tools should see this
const wxString map_name = m_sch_pin.GetDefaultNetName( path );
BOOST_CHECK_EQUAL( map_name, name );
}
/**
* Check the pin labelling when it's a power pin
*/
BOOST_AUTO_TEST_CASE( PinNumberingPower )
{
// but if we set is power...
m_lib_pin.SetType( ELECTRICAL_PINTYPE::PIN_POWER_IN );
m_parent_part.SetPower();
// the name is just the pin name
SCH_SHEET_PATH path;
const wxString pwr_name = m_sch_pin.GetDefaultNetName( path );
BOOST_CHECK_EQUAL( pwr_name, "pinname" );
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -0,0 +1,243 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 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 suite for SCH_SHEET
*/
#include <unit_test_utils/unit_test_utils.h>
// Code under test
#include <sch_sheet.h>
#include "timestamp_test_utils.h"
#include <unit_test_utils/wx_assert.h>
class TEST_SCH_SHEET_FIXTURE
{
public:
TEST_SCH_SHEET_FIXTURE() : m_sheet(), m_csheet( m_sheet )
{
}
SCH_SHEET m_sheet;
///> Can use when you need a const ref (lots of places need fixing here)
const SCH_SHEET& m_csheet;
};
/**
* Print helper.
* Not a print_log_value because old Boosts don't like that in BOOST_CHECK_EQUAL_COLLECTIONS
*/
std::ostream& operator<<( std::ostream& os, DANGLING_END_ITEM const& d )
{
os << "DANGLING_END_ITEM[ type " << d.GetType() << " @(" << d.GetPosition().x << ", "
<< d.GetPosition().y << "), item " << d.GetItem() << ", parent " << d.GetParent() << " ]";
return os;
}
bool operator==( const DANGLING_END_ITEM& aA, const DANGLING_END_ITEM& aB )
{
return aA.GetItem() == aB.GetItem()
&& aA.GetPosition() == aB.GetPosition()
&& aA.GetType() == aB.GetType()
&& aA.GetParent() == aB.GetParent();
}
bool operator!=( const DANGLING_END_ITEM& aA, const DANGLING_END_ITEM& aB )
{
return !( aA == aB );
}
/**
* Declare the test suite
*/
BOOST_FIXTURE_TEST_SUITE( SchSheet, TEST_SCH_SHEET_FIXTURE )
/**
* Check default properties
*/
BOOST_AUTO_TEST_CASE( Default )
{
BOOST_CHECK_EQUAL( m_csheet.GetPosition(), wxPoint( 0, 0 ) );
BOOST_CHECK_PREDICATE( KI_TEST::EndsInTimestamp, ( m_csheet.GetName().ToStdString() ) );
// it is it's own root sheet
BOOST_CHECK_EQUAL( m_sheet.GetRootSheet(), &m_sheet );
BOOST_CHECK_EQUAL( m_sheet.CountSheets(), 1 );
BOOST_CHECK_EQUAL( m_csheet.GetScreenCount(), 0 );
BOOST_CHECK_EQUAL( m_sheet.ComponentCount(), 0 );
}
/**
* Test adding pins to a sheet
*/
BOOST_AUTO_TEST_CASE( AddPins )
{
const wxPoint pinPos{ 42, 13 };
// we should catch null insertions
CHECK_WX_ASSERT( m_sheet.AddPin( nullptr ) );
auto newPin = std::make_unique<SCH_SHEET_PIN>( &m_sheet, pinPos, "pinname" );
// can't be const because of RemovePin (?!)
SCH_SHEET_PIN& pinRef = *newPin;
m_sheet.AddPin( newPin.release() );
// now we can find it in the list
BOOST_CHECK_EQUAL( m_sheet.HasPins(), true );
BOOST_CHECK_EQUAL( m_sheet.HasPin( "pinname" ), true );
BOOST_CHECK_EQUAL( m_sheet.HasPin( "PINname" ), true );
BOOST_CHECK_EQUAL( m_sheet.GetPin( pinPos ), &pinRef );
// check the actual list can be retrieved
// this should be const...
SCH_SHEET_PINS& pins = m_sheet.GetPins();
BOOST_CHECK_EQUAL( &pins[0], &pinRef );
// catch the bad call
CHECK_WX_ASSERT( m_sheet.RemovePin( nullptr ) );
m_sheet.RemovePin( &pinRef );
// and it's gone
BOOST_CHECK_EQUAL( m_sheet.HasPins(), false );
BOOST_CHECK_EQUAL( m_sheet.HasPin( "pinname" ), false );
BOOST_CHECK_EQUAL( m_sheet.GetPin( pinPos ), nullptr );
}
/**
* Check that pins are added and renumbered to be unique
*/
BOOST_AUTO_TEST_CASE( PinRenumbering )
{
for( int i = 0; i < 5; ++i )
{
auto pin = std::make_unique<SCH_SHEET_PIN>( &m_sheet, wxPoint{ i, i }, "name" );
// set the pins to have the same number going in
pin->SetNumber( 2 );
m_sheet.AddPin( pin.release() );
}
SCH_SHEET_PINS& pins = m_sheet.GetPins();
std::vector<int> numbers;
for( const auto& pin : pins )
{
numbers.push_back( pin.GetNumber() );
}
// and now...they are all unique
BOOST_CHECK_PREDICATE( KI_TEST::CollectionHasNoDuplicates<decltype( numbers )>, ( numbers ) );
}
struct TEST_END_CONN_PIN
{
std::string m_pin_name;
wxPoint m_pos;
};
/**
* Test the endpoint and connection point collections: we should be able to add pins, then
* have them appear as endpoints.
*/
BOOST_AUTO_TEST_CASE( EndconnectionPoints )
{
// x = zero because the pin is clamped to the left side by default
const std::vector<TEST_END_CONN_PIN> pin_defs = {
{
"1name",
{ 0, 13 },
},
{
"2name",
{ 0, 130 },
},
};
// Insert the pins into the sheet
for( const auto& pin : pin_defs )
{
m_sheet.AddPin(
std::make_unique<SCH_SHEET_PIN>( &m_sheet, pin.m_pos, pin.m_pin_name ).release() );
}
SCH_SHEET_PINS& pins = m_sheet.GetPins();
// make sure the pins made it in
BOOST_CHECK_EQUAL( pins.size(), pin_defs.size() );
// Check that the EndPoint getter gets the right things
{
std::vector<DANGLING_END_ITEM> expectedDangling;
// Construct expected from the pin, not defs, as we need the pin address
for( auto& pin : pins )
{
expectedDangling.emplace_back(
DANGLING_END_T::SHEET_LABEL_END, &pin, pin.GetPosition(), &pin );
}
std::vector<DANGLING_END_ITEM> dangling;
m_sheet.GetEndPoints( dangling );
BOOST_CHECK_EQUAL_COLLECTIONS( dangling.begin(), dangling.end(), expectedDangling.begin(),
expectedDangling.end() );
}
// And check the connection getter
{
std::vector<wxPoint> expectedConnections;
// we want to see every pin that we just added
for( const auto& pin : pin_defs )
{
expectedConnections.push_back( pin.m_pos );
}
std::vector<wxPoint> connections;
m_sheet.GetConnectionPoints( connections );
BOOST_CHECK_EQUAL_COLLECTIONS( connections.begin(), connections.end(),
expectedConnections.begin(), expectedConnections.end() );
}
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -0,0 +1,137 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 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 suite for SCH_SHEET_PATH
*/
#include <unit_test_utils/unit_test_utils.h>
// Code under test
#include <sch_sheet_path.h>
#include "timestamp_test_utils.h"
#include <sch_sheet.h>
#include <sstream>
class TEST_SCH_SHEET_PATH_FIXTURE
{
public:
TEST_SCH_SHEET_PATH_FIXTURE()
{
for( unsigned i = 0; i < 4; ++i )
{
m_sheets.emplace_back( wxPoint( i, i ) );
std::ostringstream ss;
ss << "Sheet" << i;
m_sheets[i].SetName( ss.str() );
}
// 0->1->2
m_linear.push_back( &m_sheets[0] );
m_linear.push_back( &m_sheets[1] );
m_linear.push_back( &m_sheets[2] );
}
SCH_SHEET_PATH m_empty_path;
/**
* We look at sheet 2 in the hierarchy:
* Sheets: 0 -> 1 -> 2
*/
SCH_SHEET_PATH m_linear;
/// handy store of SCH_SHEET objects
std::vector<SCH_SHEET> m_sheets;
};
/**
* Declare the test suite
*/
BOOST_FIXTURE_TEST_SUITE( SchSheetPath, TEST_SCH_SHEET_PATH_FIXTURE )
/**
* Check properties of an empty SCH_SHEET_PATH
*/
BOOST_AUTO_TEST_CASE( Empty )
{
BOOST_CHECK_EQUAL( m_empty_path.size(), 0 );
BOOST_CHECK_THROW( m_empty_path.at( 0 ), std::out_of_range );
BOOST_CHECK_EQUAL( m_empty_path.GetPageNumber(), 0 );
// These accessors return nullptr when empty (i.e. they don't crash)
BOOST_CHECK_EQUAL( m_empty_path.Last(), nullptr );
BOOST_CHECK_EQUAL( m_empty_path.LastScreen(), nullptr );
BOOST_CHECK_EQUAL( m_empty_path.LastDrawList(), nullptr );
BOOST_CHECK_EQUAL( m_empty_path.FirstDrawList(), nullptr );
BOOST_CHECK_EQUAL( m_empty_path.Path(), "/" );
BOOST_CHECK_EQUAL( m_empty_path.PathHumanReadable(), "/" );
}
/**
* Check properties of a non-empty SCH_SHEET_PATH
*/
BOOST_AUTO_TEST_CASE( NonEmpty )
{
BOOST_CHECK_EQUAL( m_linear.size(), 3 );
BOOST_CHECK_EQUAL( m_linear.at( 0 ), &m_sheets[0] );
BOOST_CHECK_EQUAL( m_linear.at( 1 ), &m_sheets[1] );
BOOST_CHECK_EQUAL( m_linear.at( 2 ), &m_sheets[2] );
BOOST_CHECK_EQUAL( m_linear.GetPageNumber(), 0 );
BOOST_CHECK_EQUAL( m_linear.Last(), &m_sheets[2] );
BOOST_CHECK_EQUAL( m_linear.LastScreen(), nullptr );
BOOST_CHECK_EQUAL( m_linear.LastDrawList(), nullptr );
BOOST_CHECK_EQUAL( m_linear.FirstDrawList(), nullptr );
// don't know what the timestamps will be, but we know the format: /<8 chars>/<8 chars>/
BOOST_CHECK_PREDICATE(
KI_TEST::IsTimestampStringWithLevels, ( m_linear.Path().ToStdString() )( 2 ) );
// Sheet0 is the root sheet and isn't in the path
BOOST_CHECK_EQUAL( m_linear.PathHumanReadable(), "/Sheet1/Sheet2/" );
}
BOOST_AUTO_TEST_CASE( Compare )
{
SCH_SHEET_PATH otherEmpty;
BOOST_CHECK( m_empty_path == otherEmpty );
BOOST_CHECK( m_empty_path != m_linear );
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -0,0 +1,82 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 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
*/
#include "timestamp_test_utils.h"
#include <unit_test_utils/unit_test_utils.h>
namespace KI_TEST
{
bool EndsInTimestamp( const std::string& aStr )
{
if( aStr.size() < 8 )
{
BOOST_TEST_INFO( "Too short to be timestamp: " << aStr.size() );
return false;
}
return IsTimeStampish( aStr.end() - 8, aStr.end() );
}
bool IsTimestampStringWithLevels( const std::string& aStr, unsigned aLevels )
{
const unsigned tsLen = 8;
const unsigned levelLen = tsLen + 1; // add the /
if( aStr.size() != aLevels * levelLen + 1 )
{
BOOST_TEST_INFO( "String is the wrong length for " << aLevels << " levels." );
return false;
}
if( aStr[0] != '/' )
{
BOOST_TEST_INFO( "Doesn't start with '/'" );
return false;
}
auto tsBegin = aStr.begin() + 1;
for( unsigned i = 0; i < aLevels; i++ )
{
if( !IsTimeStampish( tsBegin, tsBegin + tsLen ) )
{
BOOST_TEST_INFO( "Not a timeStamp at level "
<< i << ": " << std::string( tsBegin, tsBegin + tsLen ) );
return false;
}
if( *( tsBegin + tsLen ) != '/' )
{
BOOST_TEST_INFO( "level doesn't end in '/'" );
return false;
}
tsBegin += levelLen;
}
return true;
}
} // namespace KI_TEST

View File

@ -0,0 +1,82 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 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
*/
#ifndef QA_EESCHEMA_TIMESTAMP_TEST_UTILS__H
#define QA_EESCHEMA_TIMESTAMP_TEST_UTILS__H
#include <algorithm>
#include <string>
/**
* @file
* Test utilities for timestamps
*/
namespace KI_TEST
{
/**
* Predicate for checking a timestamp character
* @param aChr the character
* @return true if it's a valid timestamp char (0-9, A-F)
*/
inline bool IsTimeStampChar( char aChr )
{
return ( aChr >= 'A' && aChr <= 'F' ) || ( aChr >= '0' && aChr <= '9' );
}
/**
* Check if the string between the iterators looks like a timestamp (i.e. 8 hex digits)
*/
template <typename T>
bool IsTimeStampish( const T& aBegin, const T& aEnd )
{
// Wrong length
if( aEnd != aBegin + 8 )
return false;
// Check all chars
return std::all_of( aBegin, aEnd, IsTimeStampChar );
}
/**
* Predicate to check if a string look like it ends in a timestamp
* @param aStr the string to check
* @return true if it does
*/
bool EndsInTimestamp( const std::string& aStr );
/**
* Predicate to check a string is a timestmap path format
*
* Eg. levels=2: /1234ABCD/9878DEFC/
*
* @param aStr candidate string
* @param levels expected levels
* @return true if format matches
*/
bool IsTimestampStringWithLevels( const std::string& aStr, unsigned aLevels );
} // namespace KI_TEST
#endif // QA_EESCHEMA_TIMESTAMP_TEST_UTILS__H

View File

@ -29,6 +29,7 @@ find_package( Boost COMPONENTS unit_test_framework filesystem system REQUIRED )
set( SRCS set( SRCS
unit_test_utils.cpp unit_test_utils.cpp
wx_assert.cpp
) )
add_library( unit_test_utils STATIC ${SRCS} ) add_library( unit_test_utils STATIC ${SRCS} )
@ -37,6 +38,7 @@ target_link_libraries( unit_test_utils PUBLIC
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
${Boost_FILESYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}
${wxWidgets_LIBRARIES}
) )
target_include_directories( unit_test_utils PUBLIC target_include_directories( unit_test_utils PUBLIC

View File

@ -27,9 +27,12 @@
#include <boost/test/test_case_template.hpp> #include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <unit_test_utils/wx_assert.h>
#include <functional> #include <functional>
#include <set> #include <set>
#include <wx/gdicmn.h>
/** /**
* If HAVE_EXPECTED_FAILURES is defined, this means that * If HAVE_EXPECTED_FAILURES is defined, this means that
* boost::unit_test::expected_failures is available. * boost::unit_test::expected_failures is available.
@ -130,6 +133,43 @@ BOOST_TEST_PRINT_NAMESPACE_CLOSE
#endif #endif
BOOST_TEST_PRINT_NAMESPACE_OPEN
{
/**
* Boost print helper for generic vectors
*/
template <typename T>
struct print_log_value<std::vector<T>>
{
inline void operator()( std::ostream& os, std::vector<T> const& aVec )
{
os << "std::vector size " << aVec.size() << "[";
for( const auto& i : aVec )
{
os << "\n ";
print_log_value<T>()( os, i );
}
os << "]";
}
};
/**
* Boost print helper for wxPoint. Note operator<< for this type doesn't
* exist in non-DEBUG builds.
*/
template <>
struct print_log_value<wxPoint>
{
void operator()( std::ostream& os, wxPoint const& aVec );
};
}
BOOST_TEST_PRINT_NAMESPACE_CLOSE
namespace KI_TEST namespace KI_TEST
{ {
@ -231,6 +271,19 @@ void CheckUnorderedMatches(
} }
/**
* Predicate to check a collection has no duplicate elements
*/
template <typename T>
bool CollectionHasNoDuplicates( const T& aCollection )
{
T sorted = aCollection;
std::sort( sorted.begin(), sorted.end() );
return std::adjacent_find( sorted.begin(), sorted.end() ) == sorted.end();
}
/** /**
* Get a simple string "aIn -> aOut". * Get a simple string "aIn -> aOut".
* *
@ -251,6 +304,18 @@ std::string InOutString( const IN& aIn, const OUT& aOut )
return ss.str(); return ss.str();
} }
/**
* A test macro to check a wxASSERT is thrown.
*
* This only happens in DEBUG builds, so prevent test failures in Release builds
* by using this macro.
*/
#ifdef DEBUG
#define CHECK_WX_ASSERT( STATEMENT ) BOOST_CHECK_THROW( STATEMENT, KI_TEST::WX_ASSERT_ERROR );
#else
#define CHECK_WX_ASSERT( STATEMENT )
#endif
} // namespace KI_TEST } // namespace KI_TEST
#endif // UNIT_TEST_UTILS__H #endif // UNIT_TEST_UTILS__H

View File

@ -0,0 +1,67 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 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
*/
#ifndef UNIT_TEST_UTILS_WX_ASSERT__H
#define UNIT_TEST_UTILS_WX_ASSERT__H
#include <wx/string.h>
#include <exception>
#include <string>
namespace KI_TEST
{
/**
* An exception class to represent a WX assertion.
*
* In normal programs, this is popped as a dialog, but in unit tests, it
* prints a fairly unhelpful stack trace and otherwise doesn't inform the
* test runner.
*
* We want to raise a formal exception to allow us to catch it with
* things like BOOST_CHECK_THROW if expected, or for the test case to fail if
* not expected.
*/
class WX_ASSERT_ERROR : public std::exception
{
public:
WX_ASSERT_ERROR( const wxString& aFile, int aLine, const wxString& aFunc, const wxString& aCond,
const wxString& aMsg );
const char* what() const noexcept override;
// Public, so catchers can have a look (though be careful as the function
// names can change over time!)
std::string m_file;
int m_line;
std::string m_func;
std::string m_cond;
std::string m_msg;
std::string m_format_msg;
};
} // namespace KI_TEST
#endif // UNIT_TEST_UTILS_WX_ASSERT__H

View File

@ -21,6 +21,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
/* #include <unit_test_utils/unit_test_utils.h>
* Nothing here yet, but CMake requires *something*.
*/ BOOST_TEST_PRINT_NAMESPACE_OPEN
{
void print_log_value<wxPoint>::operator()( std::ostream& os, wxPoint const& aPt )
{
os << "WXPOINT[ x=\"" << aPt.x << "\" y=\"" << aPt.y << "\" ]";
}
}
BOOST_TEST_PRINT_NAMESPACE_CLOSE

View File

@ -0,0 +1,52 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 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
*/
#include <unit_test_utils/wx_assert.h>
#include <sstream>
namespace KI_TEST
{
WX_ASSERT_ERROR::WX_ASSERT_ERROR( const wxString& aFile, int aLine, const wxString& aFunc,
const wxString& aCond, const wxString& aMsg )
: m_file( aFile ), m_line( aLine ), m_func( aFunc ), m_cond( aCond ), m_msg( aMsg )
{
std::ostringstream ss;
ss << "WX assertion in " << m_file << ":" << m_line << "\n"
<< "in function " << m_func << "\n"
<< "failed condition: " << m_cond;
if( m_msg.size() )
ss << "\n"
<< "with message: " << m_msg;
m_format_msg = ss.str();
}
const char* WX_ASSERT_ERROR::what() const noexcept
{
return m_format_msg.c_str();
}
} // namespace KI_TEST