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
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}
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/observable.cpp
# need the mock Pgm for many functions
mocks_eeschema.cpp
eeschema_test_utils.cpp
timestamp_test_utils.cpp
# The main test entry points
test_module.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
# 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 <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()
{
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
unit_test_utils.cpp
wx_assert.cpp
)
add_library( unit_test_utils STATIC ${SRCS} )
@ -37,6 +38,7 @@ target_link_libraries( unit_test_utils PUBLIC
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${wxWidgets_LIBRARIES}
)
target_include_directories( unit_test_utils PUBLIC

View File

@ -27,9 +27,12 @@
#include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp>
#include <unit_test_utils/wx_assert.h>
#include <functional>
#include <set>
#include <wx/gdicmn.h>
/**
* If HAVE_EXPECTED_FAILURES is defined, this means that
* boost::unit_test::expected_failures is available.
@ -130,6 +133,43 @@ BOOST_TEST_PRINT_NAMESPACE_CLOSE
#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
{
@ -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".
*
@ -251,6 +304,18 @@ std::string InOutString( const IN& aIn, const OUT& aOut )
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
#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
*/
/*
* Nothing here yet, but CMake requires *something*.
*/
#include <unit_test_utils/unit_test_utils.h>
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