mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 18:23:15 +02:00
fix for issue 17429 - DRC exclusion instability
- backported from master - fix for issue-17429 - DRC unnconnected items exclusion instability - new serialization format for unconnected items (MainID and AuxID last) - RunOnUnconnectedItems sorts RN_NET edges for stability - ResolveDRCExclusions now matches unconnected items by not matching on MainID and AuxID
This commit is contained in:
parent
9ea4b899a2
commit
4a99dc1a99
@ -345,8 +345,49 @@ void BOARD::RecordDRCExclusions()
|
||||
m_designSettings->m_DrcExclusionComments[ serialized ] = marker->GetComment();
|
||||
}
|
||||
}
|
||||
|
||||
if( m_project )
|
||||
{
|
||||
if( PROJECT_FILE* projectFile = &m_project->GetProjectFile() )
|
||||
{
|
||||
if( BOARD_DESIGN_SETTINGS* prjSettings = projectFile->m_BoardSettings )
|
||||
{
|
||||
prjSettings->m_DrcExclusions = m_designSettings->m_DrcExclusions;
|
||||
prjSettings->m_DrcExclusionComments = m_designSettings->m_DrcExclusionComments;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::set<wxString>::iterator FindByFirstNFields( std::set<wxString>& strSet,
|
||||
const wxString& searchStr, char delimiter, int n )
|
||||
{
|
||||
wxString searchPrefix = searchStr;
|
||||
|
||||
// Extract first n fields from the search string
|
||||
int delimiterCount = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
while( pos < searchPrefix.length() && delimiterCount < n )
|
||||
{
|
||||
if( searchPrefix[pos] == delimiter )
|
||||
delimiterCount++;
|
||||
pos++;
|
||||
}
|
||||
|
||||
if( delimiterCount == n )
|
||||
searchPrefix = searchPrefix.Left( pos - 1 ); // Exclude the nth delimiter
|
||||
|
||||
for( auto it = strSet.begin(); it != strSet.end(); ++it )
|
||||
{
|
||||
if( it->StartsWith( searchPrefix + delimiter ) || *it == searchPrefix )
|
||||
{
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
return strSet.end();
|
||||
}
|
||||
|
||||
std::vector<PCB_MARKER*> BOARD::ResolveDRCExclusions( bool aCreateMarkers )
|
||||
{
|
||||
@ -358,16 +399,39 @@ std::vector<PCB_MARKER*> BOARD::ResolveDRCExclusions( bool aCreateMarkers )
|
||||
|
||||
for( PCB_MARKER* marker : GetBoard()->Markers() )
|
||||
{
|
||||
std::set<wxString>::iterator it;
|
||||
wxString serialized = marker->SerializeToString();
|
||||
std::set<wxString>::iterator it = exclusions.find( serialized );
|
||||
wxString matchedExclusion;
|
||||
|
||||
if( !serialized.Contains( "unconnected_items" ) )
|
||||
{
|
||||
it = exclusions.find( serialized );
|
||||
if( it != exclusions.end() )
|
||||
{
|
||||
matchedExclusion = *it;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int numberOfFieldsExcludingIds = 3;
|
||||
const char delimiter = '|';
|
||||
it = FindByFirstNFields( exclusions, serialized, delimiter,
|
||||
numberOfFieldsExcludingIds );
|
||||
if( it != exclusions.end() )
|
||||
{
|
||||
matchedExclusion = *it;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
if( it != exclusions.end() )
|
||||
{
|
||||
marker->SetExcluded( true, comments[ serialized ] );
|
||||
marker->SetExcluded( true, comments[matchedExclusion] );
|
||||
|
||||
// Exclusion still valid; store back to BOARD_DESIGN_SETTINGS
|
||||
m_designSettings->m_DrcExclusions.insert( serialized );
|
||||
m_designSettings->m_DrcExclusionComments[ serialized ] = comments[ serialized ];
|
||||
m_designSettings->m_DrcExclusions.insert( matchedExclusion );
|
||||
m_designSettings->m_DrcExclusionComments[matchedExclusion] = comments[matchedExclusion];
|
||||
|
||||
exclusions.erase( it );
|
||||
}
|
||||
|
@ -86,6 +86,42 @@ public:
|
||||
return m_weight < aOther.m_weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison operator for std::stable_sort.
|
||||
*
|
||||
* @param aOther the other edge to compare.
|
||||
* @return true if this edge should come before aOther in the sorted order.
|
||||
*
|
||||
* Comparison order:
|
||||
* 1. Compare source nodes by position (x, then y)
|
||||
* 2. Then compare by weight
|
||||
* 3. Then by visibility
|
||||
* 4. If everything is equal, return false for stable ordering
|
||||
*/
|
||||
bool StableSortCompare( const CN_EDGE& aOther ) const
|
||||
{
|
||||
const VECTOR2I& thisPos = GetSourcePos();
|
||||
const VECTOR2I& otherPos = aOther.GetSourcePos();
|
||||
|
||||
// First compare by source node position
|
||||
if( thisPos.x != otherPos.x )
|
||||
return thisPos.x < otherPos.x;
|
||||
|
||||
if( thisPos.y != otherPos.y )
|
||||
return thisPos.y < otherPos.y;
|
||||
|
||||
// Then compare by weight
|
||||
if( m_weight != aOther.m_weight )
|
||||
return m_weight < aOther.m_weight;
|
||||
|
||||
// Then by visibility
|
||||
if( m_visible != aOther.m_visible )
|
||||
return m_visible && !aOther.m_visible;
|
||||
|
||||
// If everything is equal, return false for stable ordering
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<const CN_ANCHOR> GetSourceNode() const { return m_source; }
|
||||
std::shared_ptr<const CN_ANCHOR> GetTargetNode() const { return m_target; }
|
||||
|
||||
|
@ -1184,6 +1184,7 @@ void DIALOG_DRC::ExcludeMarker()
|
||||
return;
|
||||
|
||||
RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( m_markerDataView->GetCurrentItem() );
|
||||
|
||||
PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() );
|
||||
|
||||
if( marker && marker->GetSeverity() != RPT_SEVERITY_EXCLUSION )
|
||||
|
@ -23,6 +23,7 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "connectivity/connectivity_data.h"
|
||||
#include <bitmaps.h>
|
||||
#include <base_units.h>
|
||||
#include <eda_draw_frame.h>
|
||||
@ -107,6 +108,17 @@ wxString PCB_MARKER::SerializeToString() const
|
||||
m_rcItem->GetMainItemID().AsString(),
|
||||
LayerName( m_layer ) );
|
||||
}
|
||||
else if( m_rcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
|
||||
{
|
||||
PCB_LAYER_ID layer = m_layer;
|
||||
if( m_layer == UNDEFINED_LAYER )
|
||||
layer = F_Cu;
|
||||
|
||||
return wxString::Format( wxT( "%s|%d|%d|%s|%d|%s|%s" ), m_rcItem->GetSettingsKey(), m_Pos.x,
|
||||
m_Pos.y, LayerName( layer ), GetMarkerType(),
|
||||
m_rcItem->GetMainItemID().AsString(),
|
||||
m_rcItem->GetAuxItemID().AsString() );
|
||||
}
|
||||
else if( m_rcItem->GetErrorCode() == DRCE_STARVED_THERMAL )
|
||||
{
|
||||
return wxString::Format( wxT( "%s|%d|%d|%s|%s|%s" ),
|
||||
@ -171,6 +183,18 @@ PCB_MARKER* PCB_MARKER::DeserializeFromString( const wxString& data )
|
||||
drcItem->SetItems( KIID( props[3] ) );
|
||||
markerLayer = getMarkerLayer( props[4] );
|
||||
}
|
||||
else if( drcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
|
||||
{
|
||||
// Pre-9.0.3 versions didn't have KIIDs as last two properties to allow sorting stability
|
||||
if( props.size() < 6 )
|
||||
{
|
||||
drcItem->SetItems( KIID( props[3] ), KIID( props[4] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
drcItem->SetItems( KIID( props[5] ), KIID( props[6] ) );
|
||||
}
|
||||
}
|
||||
else if( drcItem->GetErrorCode() == DRCE_STARVED_THERMAL )
|
||||
{
|
||||
drcItem->SetItems( KIID( props[3] ), KIID( props[4] ) );
|
||||
@ -204,9 +228,7 @@ void PCB_MARKER::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_
|
||||
case RPT_SEVERITY_IGNORE:
|
||||
aList.emplace_back( _( "Severity" ), _( "Ignore" ) );
|
||||
break;
|
||||
case RPT_SEVERITY_WARNING:
|
||||
aList.emplace_back( _( "Severity" ), _( "Warning" ) );
|
||||
break;
|
||||
case RPT_SEVERITY_WARNING: aList.emplace_back( _( "Severity" ), _( "Warning" ) ); break;
|
||||
case RPT_SEVERITY_ERROR:
|
||||
aList.emplace_back( _( "Severity" ), _( "Error" ) );
|
||||
break;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#ifndef RATSNEST_DATA_H
|
||||
#define RATSNEST_DATA_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <core/typeinfo.h>
|
||||
#include <math/box2.h>
|
||||
|
||||
@ -93,8 +94,28 @@ public:
|
||||
|
||||
unsigned int GetNodeCount() const { return m_nodes.size(); }
|
||||
|
||||
const std::vector<CN_EDGE>& GetEdges() const { return m_rnEdges; }
|
||||
std::vector<CN_EDGE>& GetEdges() { return m_rnEdges; }
|
||||
const std::vector<CN_EDGE>& GetEdges() const
|
||||
{
|
||||
// Use a const_cast to allow sorting in the const method
|
||||
// This is safe because we're not changing the logical content of the vector,
|
||||
// just the ordering of elements
|
||||
std::stable_sort( const_cast<std::vector<CN_EDGE>&>( m_rnEdges ).begin(),
|
||||
const_cast<std::vector<CN_EDGE>&>( m_rnEdges ).end(),
|
||||
[]( const CN_EDGE& a, const CN_EDGE& b )
|
||||
{
|
||||
return a.StableSortCompare( b );
|
||||
} );
|
||||
return m_rnEdges;
|
||||
}
|
||||
std::vector<CN_EDGE>& GetEdges()
|
||||
{
|
||||
std::stable_sort( m_rnEdges.begin(), m_rnEdges.end(),
|
||||
[]( const CN_EDGE& a, const CN_EDGE& b )
|
||||
{
|
||||
return a.StableSortCompare( b );
|
||||
} );
|
||||
return m_rnEdges;
|
||||
}
|
||||
|
||||
bool NearestBicoloredPair( RN_NET* aOtherNet, VECTOR2I& aPos1, VECTOR2I& aPos2 ) const;
|
||||
|
||||
|
8082
qa/data/pcbnew/issue17429.kicad_pcb
Normal file
8082
qa/data/pcbnew/issue17429.kicad_pcb
Normal file
File diff suppressed because it is too large
Load Diff
960
qa/data/pcbnew/issue17429.kicad_pro
Normal file
960
qa/data/pcbnew/issue17429.kicad_pro
Normal file
@ -0,0 +1,960 @@
|
||||
{
|
||||
"board": {
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
"board_outline_line_width": 0.05,
|
||||
"copper_line_width": 0.2,
|
||||
"copper_text_italic": false,
|
||||
"copper_text_size_h": 1.5,
|
||||
"copper_text_size_v": 1.5,
|
||||
"copper_text_thickness": 0.3,
|
||||
"copper_text_upright": false,
|
||||
"courtyard_line_width": 0.05,
|
||||
"dimension_precision": 4,
|
||||
"dimension_units": 3,
|
||||
"dimensions": {
|
||||
"arrow_length": 1270000,
|
||||
"extension_offset": 500000,
|
||||
"keep_text_aligned": true,
|
||||
"suppress_zeroes": false,
|
||||
"text_position": 0,
|
||||
"units_format": 1
|
||||
},
|
||||
"fab_line_width": 0.1,
|
||||
"fab_text_italic": false,
|
||||
"fab_text_size_h": 1.0,
|
||||
"fab_text_size_v": 1.0,
|
||||
"fab_text_thickness": 0.15,
|
||||
"fab_text_upright": false,
|
||||
"other_line_width": 0.1,
|
||||
"other_text_italic": false,
|
||||
"other_text_size_h": 1.0,
|
||||
"other_text_size_v": 1.0,
|
||||
"other_text_thickness": 0.15,
|
||||
"other_text_upright": false,
|
||||
"pads": {
|
||||
"drill": 0.0,
|
||||
"height": 1.8,
|
||||
"width": 1.2
|
||||
},
|
||||
"silk_line_width": 0.12,
|
||||
"silk_text_italic": false,
|
||||
"silk_text_size_h": 1.0,
|
||||
"silk_text_size_v": 1.0,
|
||||
"silk_text_thickness": 0.15,
|
||||
"silk_text_upright": false,
|
||||
"zones": {
|
||||
"45_degree_only": false,
|
||||
"min_clearance": 0.508
|
||||
}
|
||||
},
|
||||
"diff_pair_dimensions": [
|
||||
{
|
||||
"gap": 0.0,
|
||||
"via_gap": 0.0,
|
||||
"width": 0.0
|
||||
},
|
||||
{
|
||||
"gap": 0.127,
|
||||
"via_gap": 0.127,
|
||||
"width": 0.154
|
||||
}
|
||||
],
|
||||
"drc_exclusions": [
|
||||
[
|
||||
"unconnected_items|115460000|155800000|F.Cu|4|221e4293-1dad-4f91-a869-d39634ca7647|74f57098-b83f-40bf-9a16-2adad8dcb50b",
|
||||
""
|
||||
],
|
||||
[
|
||||
"unconnected_items|115460000|159000000|F.Cu|4|e5b239f2-afe1-4783-8a0b-bf234fea22df|113054e2-9827-4c9f-a8ee-9dead8ac7000",
|
||||
""
|
||||
],
|
||||
[
|
||||
"unconnected_items|115460000|161400000|F.Cu|4|cff4aed3-271d-45c0-a3c4-046e16b7f260|d384076d-8217-4237-8a13-86ece3667b99",
|
||||
""
|
||||
],
|
||||
[
|
||||
"unconnected_items|115460000|165400000|F.Cu|4|74da9126-9d2f-4c1d-b8a1-021e27bcafbe|519972f5-567a-42f1-a038-c4960d508559",
|
||||
""
|
||||
],
|
||||
[
|
||||
"unconnected_items|115460000|166200000|F.Cu|4|3f4da512-b026-405d-a045-87c506a3ef63|87b98e0c-3614-42af-8982-59c036259cc8",
|
||||
""
|
||||
],
|
||||
[
|
||||
"unconnected_items|115460000|168600000|F.Cu|4|742ed10c-8c85-48f3-b92b-a749b92a3c8c|c7b6b524-0fb3-41c8-9654-229fe7bd19e7",
|
||||
""
|
||||
],
|
||||
[
|
||||
"unconnected_items|118540000|154600000|F.Cu|4|69b8910c-2ead-48f4-9358-4be4a4a0663e|539d8a1e-5e53-4252-9a33-1eb2c9717a4b",
|
||||
""
|
||||
],
|
||||
[
|
||||
"unconnected_items|118540000|155000000|F.Cu|4|d3546ca6-3035-4598-a37d-c76932d318fd|3eab64eb-a1f0-434d-968b-cd0f04f08b5b",
|
||||
""
|
||||
],
|
||||
[
|
||||
"unconnected_items|118540000|158200000|F.Cu|4|e3fa1053-01eb-44fc-ac99-4eb5213cc1c6|ef60360a-a7b4-4448-90c3-e2532f9767b4",
|
||||
""
|
||||
],
|
||||
[
|
||||
"unconnected_items|118540000|167800000|F.Cu|4|18383a8f-1c88-4048-a569-5b451205ef1b|c717c398-f57e-451d-8ffd-3863c703056a",
|
||||
""
|
||||
]
|
||||
],
|
||||
"meta": {
|
||||
"filename": "board_design_settings.json",
|
||||
"version": 2
|
||||
},
|
||||
"rule_severities": {
|
||||
"annular_width": "error",
|
||||
"clearance": "error",
|
||||
"connection_width": "warning",
|
||||
"copper_edge_clearance": "error",
|
||||
"copper_sliver": "warning",
|
||||
"courtyards_overlap": "error",
|
||||
"creepage": "error",
|
||||
"diff_pair_gap_out_of_range": "error",
|
||||
"diff_pair_uncoupled_length_too_long": "error",
|
||||
"drill_out_of_range": "error",
|
||||
"duplicate_footprints": "warning",
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_filters_mismatch": "ignore",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "error",
|
||||
"hole_clearance": "error",
|
||||
"hole_near_hole": "error",
|
||||
"hole_to_hole": "error",
|
||||
"holes_co_located": "warning",
|
||||
"invalid_outline": "error",
|
||||
"isolated_copper": "warning",
|
||||
"item_on_disabled_layer": "error",
|
||||
"items_not_allowed": "error",
|
||||
"length_out_of_range": "error",
|
||||
"lib_footprint_issues": "warning",
|
||||
"lib_footprint_mismatch": "warning",
|
||||
"malformed_courtyard": "error",
|
||||
"microvia_drill_out_of_range": "error",
|
||||
"mirrored_text_on_front_layer": "warning",
|
||||
"missing_courtyard": "ignore",
|
||||
"missing_footprint": "warning",
|
||||
"net_conflict": "warning",
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "ignore",
|
||||
"padstack": "error",
|
||||
"pth_inside_courtyard": "ignore",
|
||||
"shorting_items": "error",
|
||||
"silk_edge_clearance": "warning",
|
||||
"silk_over_copper": "warning",
|
||||
"silk_overlap": "warning",
|
||||
"skew_out_of_range": "error",
|
||||
"solder_mask_bridge": "error",
|
||||
"starved_thermal": "error",
|
||||
"text_height": "warning",
|
||||
"text_on_edge_cuts": "error",
|
||||
"text_thickness": "warning",
|
||||
"through_hole_pad_without_hole": "error",
|
||||
"too_many_vias": "error",
|
||||
"track_angle": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_segment_length": "error",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
"zones_intersect": "error"
|
||||
},
|
||||
"rules": {
|
||||
"allow_blind_buried_vias": true,
|
||||
"allow_microvias": true,
|
||||
"max_error": 0.005,
|
||||
"min_clearance": 0.127,
|
||||
"min_connection": 0.0,
|
||||
"min_copper_edge_clearance": 0.254,
|
||||
"min_groove_width": 0.0,
|
||||
"min_hole_clearance": 0.127,
|
||||
"min_hole_to_hole": 0.254,
|
||||
"min_microvia_diameter": 0.2,
|
||||
"min_microvia_drill": 0.1,
|
||||
"min_resolved_spokes": 2,
|
||||
"min_silk_clearance": 0.0,
|
||||
"min_text_height": 0.8,
|
||||
"min_text_thickness": 0.08,
|
||||
"min_through_hole_diameter": 0.254,
|
||||
"min_track_width": 0.15,
|
||||
"min_via_annular_width": 0.1016,
|
||||
"min_via_diameter": 0.254,
|
||||
"solder_mask_to_copper_clearance": 0.0,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
"teardrop_options": [
|
||||
{
|
||||
"td_onpthpad": true,
|
||||
"td_onroundshapesonly": false,
|
||||
"td_onsmdpad": true,
|
||||
"td_ontrackend": false,
|
||||
"td_onvia": true
|
||||
}
|
||||
],
|
||||
"teardrop_parameters": [
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_round_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_rect_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_track_end",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
}
|
||||
],
|
||||
"track_widths": [
|
||||
0.0,
|
||||
0.127,
|
||||
0.154,
|
||||
0.254,
|
||||
0.4,
|
||||
0.5
|
||||
],
|
||||
"tuning_pattern_settings": {
|
||||
"diff_pair_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 1.0
|
||||
},
|
||||
"diff_pair_skew_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
},
|
||||
"single_track_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
}
|
||||
},
|
||||
"via_dimensions": [
|
||||
{
|
||||
"diameter": 0.0,
|
||||
"drill": 0.0
|
||||
},
|
||||
{
|
||||
"diameter": 0.3483,
|
||||
"drill": 0.2467
|
||||
},
|
||||
{
|
||||
"diameter": 0.3556,
|
||||
"drill": 0.254
|
||||
},
|
||||
{
|
||||
"diameter": 0.4572,
|
||||
"drill": 0.254
|
||||
},
|
||||
{
|
||||
"diameter": 0.5,
|
||||
"drill": 0.375
|
||||
}
|
||||
],
|
||||
"zones_allow_external_fillets": false,
|
||||
"zones_use_no_outline": true
|
||||
},
|
||||
"ipc2581": {
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": ""
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [
|
||||
{
|
||||
"activeLayer": -2,
|
||||
"flipBoard": false,
|
||||
"layers": [
|
||||
4,
|
||||
8,
|
||||
10,
|
||||
12,
|
||||
14,
|
||||
16,
|
||||
18,
|
||||
20,
|
||||
22,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
28,
|
||||
30,
|
||||
32,
|
||||
34,
|
||||
36,
|
||||
38,
|
||||
40,
|
||||
42,
|
||||
44,
|
||||
46,
|
||||
48,
|
||||
50,
|
||||
52,
|
||||
54,
|
||||
56,
|
||||
58,
|
||||
60,
|
||||
62
|
||||
],
|
||||
"name": "In.1 Cu - AGND",
|
||||
"renderLayers": [
|
||||
"vias",
|
||||
"footprint_text",
|
||||
"footprint_anchors",
|
||||
"ratsnest",
|
||||
"grid",
|
||||
"footprints_front",
|
||||
"footprints_back",
|
||||
"footprint_values",
|
||||
"footprint_references",
|
||||
"tracks",
|
||||
"drc_errors",
|
||||
"drawing_sheet",
|
||||
"bitmaps",
|
||||
"pads",
|
||||
"zones",
|
||||
"drc_warnings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"activeLayer": -2,
|
||||
"flipBoard": false,
|
||||
"layers": [
|
||||
6,
|
||||
8,
|
||||
10,
|
||||
12,
|
||||
14,
|
||||
16,
|
||||
18,
|
||||
20,
|
||||
22,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
28,
|
||||
30,
|
||||
32,
|
||||
34,
|
||||
36,
|
||||
38,
|
||||
40,
|
||||
42,
|
||||
44,
|
||||
46,
|
||||
48,
|
||||
50,
|
||||
52,
|
||||
54,
|
||||
56,
|
||||
58,
|
||||
60,
|
||||
62
|
||||
],
|
||||
"name": "In.2 Cu - DVDD / AVDD / AVSS",
|
||||
"renderLayers": [
|
||||
"vias",
|
||||
"footprint_text",
|
||||
"footprint_anchors",
|
||||
"ratsnest",
|
||||
"grid",
|
||||
"footprints_front",
|
||||
"footprints_back",
|
||||
"footprint_values",
|
||||
"footprint_references",
|
||||
"tracks",
|
||||
"drc_errors",
|
||||
"drawing_sheet",
|
||||
"bitmaps",
|
||||
"pads",
|
||||
"zones",
|
||||
"drc_warnings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"activeLayer": -2,
|
||||
"flipBoard": false,
|
||||
"layers": [
|
||||
14,
|
||||
16,
|
||||
18,
|
||||
20,
|
||||
22,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
28,
|
||||
30,
|
||||
32,
|
||||
34,
|
||||
36,
|
||||
37,
|
||||
38,
|
||||
40,
|
||||
42,
|
||||
44,
|
||||
46,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
51,
|
||||
52,
|
||||
53,
|
||||
54,
|
||||
55,
|
||||
56,
|
||||
58,
|
||||
60,
|
||||
62
|
||||
],
|
||||
"name": "Layer 6 - x signal",
|
||||
"renderLayers": [
|
||||
"vias",
|
||||
"footprint_text",
|
||||
"footprint_anchors",
|
||||
"footprints_front",
|
||||
"footprints_back",
|
||||
"footprint_values",
|
||||
"footprint_references",
|
||||
"tracks",
|
||||
"drc_errors",
|
||||
"drawing_sheet",
|
||||
"bitmaps",
|
||||
"pads",
|
||||
"zones",
|
||||
"drc_warnings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"activeLayer": -2,
|
||||
"flipBoard": false,
|
||||
"layers": [
|
||||
8,
|
||||
10,
|
||||
16,
|
||||
18,
|
||||
20,
|
||||
22,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
31,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36,
|
||||
37,
|
||||
38,
|
||||
40,
|
||||
42,
|
||||
44,
|
||||
46,
|
||||
48,
|
||||
50,
|
||||
52,
|
||||
54,
|
||||
56,
|
||||
58,
|
||||
60,
|
||||
62
|
||||
],
|
||||
"name": "horiz-vert inner",
|
||||
"renderLayers": [
|
||||
"vias",
|
||||
"footprint_text",
|
||||
"footprint_anchors",
|
||||
"footprints_front",
|
||||
"footprints_back",
|
||||
"footprint_values",
|
||||
"footprint_references",
|
||||
"tracks",
|
||||
"drc_errors",
|
||||
"drawing_sheet",
|
||||
"bitmaps",
|
||||
"pads",
|
||||
"zones",
|
||||
"drc_warnings"
|
||||
]
|
||||
}
|
||||
],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
"erc": {
|
||||
"erc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"pin_map": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
]
|
||||
],
|
||||
"rule_severities": {
|
||||
"bus_definition_conflict": "error",
|
||||
"bus_entry_needed": "error",
|
||||
"bus_to_bus_conflict": "error",
|
||||
"bus_to_net_conflict": "error",
|
||||
"conflicting_netclasses": "error",
|
||||
"different_unit_footprint": "error",
|
||||
"different_unit_net": "error",
|
||||
"duplicate_reference": "error",
|
||||
"duplicate_sheet_names": "error",
|
||||
"endpoint_off_grid": "warning",
|
||||
"extra_units": "error",
|
||||
"footprint_filter": "ignore",
|
||||
"footprint_link_issues": "warning",
|
||||
"four_way_junction": "ignore",
|
||||
"global_label_dangling": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"label_dangling": "error",
|
||||
"label_multiple_wires": "warning",
|
||||
"lib_symbol_issues": "warning",
|
||||
"lib_symbol_mismatch": "warning",
|
||||
"missing_bidi_pin": "warning",
|
||||
"missing_input_pin": "warning",
|
||||
"missing_power_pin": "error",
|
||||
"missing_unit": "warning",
|
||||
"multiple_net_names": "warning",
|
||||
"net_not_bus_member": "warning",
|
||||
"no_connect_connected": "warning",
|
||||
"no_connect_dangling": "warning",
|
||||
"pin_not_connected": "error",
|
||||
"pin_not_driven": "error",
|
||||
"pin_to_pin": "warning",
|
||||
"power_pin_not_driven": "error",
|
||||
"same_local_global_label": "warning",
|
||||
"similar_label_and_power": "warning",
|
||||
"similar_labels": "warning",
|
||||
"similar_power": "warning",
|
||||
"simulation_model_issue": "ignore",
|
||||
"single_global_label": "ignore",
|
||||
"unannotated": "error",
|
||||
"unconnected_wire_endpoint": "warning",
|
||||
"unit_value_mismatch": "error",
|
||||
"unresolved_variable": "error",
|
||||
"wire_dangling": "error"
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"pinned_footprint_libs": [],
|
||||
"pinned_symbol_libs": []
|
||||
},
|
||||
"meta": {
|
||||
"filename": "issue17429-9.0.kicad_pro",
|
||||
"version": 3
|
||||
},
|
||||
"net_settings": {
|
||||
"classes": [
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.127,
|
||||
"diff_pair_gap": 0.127,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.154,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.254,
|
||||
"microvia_drill": 0.4572,
|
||||
"name": "Default",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.154,
|
||||
"via_diameter": 0.4572,
|
||||
"via_drill": 0.254,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 4
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
"netclass_patterns": []
|
||||
},
|
||||
"pcbnew": {
|
||||
"last_paths": {
|
||||
"gencad": "",
|
||||
"idf": "",
|
||||
"netlist": "",
|
||||
"plot": "",
|
||||
"pos_files": "",
|
||||
"specctra_dsn": "",
|
||||
"step": "",
|
||||
"svg": "",
|
||||
"vrml": ""
|
||||
},
|
||||
"page_layout_descr_file": ""
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"bom_export_filename": "",
|
||||
"bom_fmt_presets": [],
|
||||
"bom_fmt_settings": {
|
||||
"field_delimiter": ",",
|
||||
"keep_line_breaks": false,
|
||||
"keep_tabs": false,
|
||||
"name": "CSV",
|
||||
"ref_delimiter": ",",
|
||||
"ref_range_delimiter": "",
|
||||
"string_delimiter": "\""
|
||||
},
|
||||
"bom_presets": [],
|
||||
"bom_settings": {
|
||||
"exclude_dnp": false,
|
||||
"fields_ordered": [
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Reference",
|
||||
"name": "Reference",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Value",
|
||||
"name": "Value",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Datasheet",
|
||||
"name": "Datasheet",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Footprint",
|
||||
"name": "Footprint",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Qty",
|
||||
"name": "${QUANTITY}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "DNP",
|
||||
"name": "${DNP}",
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"filter_string": "",
|
||||
"group_symbols": true,
|
||||
"include_excluded_from_bom": false,
|
||||
"name": "Grouped By Value",
|
||||
"sort_asc": true,
|
||||
"sort_field": "Reference"
|
||||
},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
"dashed_lines_gap_length_ratio": 3.0,
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"field_names": [],
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
"intersheets_ref_show": false,
|
||||
"intersheets_ref_suffix": "",
|
||||
"junction_size_choice": 3,
|
||||
"label_size_ratio": 0.375,
|
||||
"operating_point_overlay_i_precision": 3,
|
||||
"operating_point_overlay_i_range": "~A",
|
||||
"operating_point_overlay_v_precision": 3,
|
||||
"operating_point_overlay_v_range": "~V",
|
||||
"overbar_offset_ratio": 1.23,
|
||||
"pin_symbol_size": 25.0,
|
||||
"text_offset_ratio": 0.15
|
||||
},
|
||||
"legacy_lib_dir": "",
|
||||
"legacy_lib_list": [],
|
||||
"meta": {
|
||||
"version": 1
|
||||
},
|
||||
"net_format_name": "",
|
||||
"ngspice": {
|
||||
"fix_include_paths": true,
|
||||
"fix_passive_vals": false,
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"model_mode": 0,
|
||||
"workbook_filename": ""
|
||||
},
|
||||
"page_layout_descr_file": "empty.kicad_wks",
|
||||
"plot_directory": "",
|
||||
"space_save_all_events": true,
|
||||
"spice_adjust_passive_values": false,
|
||||
"spice_current_sheet_as_root": false,
|
||||
"spice_external_command": "spice \"%I\"",
|
||||
"spice_model_current_sheet_as_root": true,
|
||||
"spice_save_all_currents": false,
|
||||
"spice_save_all_dissipations": false,
|
||||
"spice_save_all_voltages": false,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
"9e566840-a51e-4471-a3e7-2b665aaf43dc",
|
||||
"Root"
|
||||
],
|
||||
[
|
||||
"3f8adf54-3826-4b3b-9b7c-b7e31dd15219",
|
||||
"MIM 1 Connectors"
|
||||
],
|
||||
[
|
||||
"959ea2eb-ede7-4845-a5e0-f5d704ded284",
|
||||
"USB C Power Delivery and USB connectors"
|
||||
],
|
||||
[
|
||||
"876907c5-4303-4331-b98b-edb1b9148224",
|
||||
"Jupiter CM1 Connectors"
|
||||
],
|
||||
[
|
||||
"b65d545f-09b3-49cc-ba93-08687c0e9554",
|
||||
"SD Card , JTAG Connectors, UUID EEPROM"
|
||||
],
|
||||
[
|
||||
"0836bfb4-6518-4cf8-bc77-6e6f74f1461f",
|
||||
"Electrode Connector"
|
||||
],
|
||||
[
|
||||
"7178ddd0-9526-4df3-bf3a-3aae1a195dc4",
|
||||
"System LED and Accelerometer"
|
||||
],
|
||||
[
|
||||
"f74cc5db-afe7-427c-9803-d67b8c3889e9",
|
||||
"Battery Charger, Fuel Gauge, Regulators"
|
||||
],
|
||||
[
|
||||
"b57c4078-aa32-4f39-b920-da21bd34b003",
|
||||
"User Connectors and Optoisolation"
|
||||
],
|
||||
[
|
||||
"1c95cdbc-eb19-4e2d-a613-dc22a92780a9",
|
||||
"MIM 0 Connectors"
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
}
|
@ -84,9 +84,17 @@ void LoadBoard( SETTINGS_MANAGER& aSettingsManager, const wxString& aRelPath,
|
||||
wxFileName rulesFile( absPath + ".kicad_dru" );
|
||||
|
||||
if( projectFile.Exists() )
|
||||
{
|
||||
aSettingsManager.LoadProject( projectFile.GetFullPath() );
|
||||
BOOST_TEST_MESSAGE( "Loading project file: " << projectFile.GetFullPath() );
|
||||
}
|
||||
else if( legacyProject.Exists() )
|
||||
{
|
||||
aSettingsManager.LoadProject( legacyProject.GetFullPath() );
|
||||
BOOST_TEST_MESSAGE( "Loading project file: " << projectFile.GetFullPath() );
|
||||
}
|
||||
else
|
||||
BOOST_TEST_MESSAGE( "Could not load project: " << projectFile.GetFullPath() );
|
||||
|
||||
BOOST_TEST_MESSAGE( "Loading board file: " << boardPath );
|
||||
|
||||
|
@ -65,6 +65,7 @@ set( QA_PCBNEW_SRCS
|
||||
drc/test_drc_skew.cpp
|
||||
drc/test_drc_component_classes.cpp
|
||||
drc/test_drc_incorrect_text_mirror.cpp
|
||||
drc/test_drc_unconnected_items_exclusion_loss.cpp
|
||||
|
||||
pcb_io/altium/test_altium_rule_transformer.cpp
|
||||
pcb_io/altium/test_altium_pcblib_import.cpp
|
||||
|
@ -0,0 +1,330 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The KiCad Developers, see AUTHORS.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 <filesystem>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <board_design_settings.h>
|
||||
#include <board.h>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <drc/drc_item.h>
|
||||
#include <footprint.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_edit_frame.h>
|
||||
#include <pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h>
|
||||
#include <pcb_io/pcb_io_mgr.h>
|
||||
#include <pcb_io/pcb_io.h>
|
||||
#include <pcb_marker.h>
|
||||
#include <pcb_track.h>
|
||||
#include <pcbnew_utils/board_file_utils.h>
|
||||
#include <pcbnew_utils/board_test_utils.h>
|
||||
#include <project.h>
|
||||
#include <project/project_file.h>
|
||||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <tool/tool_manager.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
struct FileCleaner
|
||||
{
|
||||
std::vector<wxString> m_files_to_delete;
|
||||
|
||||
FileCleaner() = default;
|
||||
~FileCleaner()
|
||||
{
|
||||
for( const auto& f_path : m_files_to_delete )
|
||||
{
|
||||
if( wxFileName::Exists( f_path ) )
|
||||
{
|
||||
if( !wxRemoveFile( f_path ) )
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Warning: Failed to delete temporary file " << f_path );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddFile( const wxString& f_path ) { m_files_to_delete.push_back( f_path ); }
|
||||
};
|
||||
|
||||
struct DRC_BASE_FIXTURE
|
||||
{
|
||||
DRC_BASE_FIXTURE() :
|
||||
m_settingsManager( true /* headless */ )
|
||||
{
|
||||
}
|
||||
|
||||
std::string generate_uuid();
|
||||
bool SaveBoardToFile( BOARD* board, const wxString& filename );
|
||||
void loadBoardAndVerifyInitialExclusions( const wxString& aBoardNameStem, int aExpectedInitialExclusions );
|
||||
void createAndVerifyInitialExclusionMarkers();
|
||||
int createAndVerifyAdditionalUnconnectedExclusions( int aAdditionalExclusions, int aInitialExclusions );
|
||||
void runDrcOnBoard();
|
||||
void saveBoardAndProjectToTempFiles( const wxString& aBoardNameStem, FileCleaner& aCleaner,
|
||||
wxString& aTempBoardFullPath, wxString& aTempProjectFullPath,
|
||||
wxString& aTempBoardStemName );
|
||||
void reloadBoardAndVerifyExclusions( const wxString& aTempBoardStemName, int aExpectedExclusions );
|
||||
|
||||
|
||||
SETTINGS_MANAGER m_settingsManager;
|
||||
std::unique_ptr<BOARD> m_board;
|
||||
};
|
||||
|
||||
struct DRC_REGRESSION_TEST_FIXTURE : public DRC_BASE_FIXTURE
|
||||
{
|
||||
DRC_REGRESSION_TEST_FIXTURE() :
|
||||
DRC_BASE_FIXTURE()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct DRC_UNCONNECTED_SAVE_FIXTURE : public DRC_BASE_FIXTURE
|
||||
{
|
||||
DRC_UNCONNECTED_SAVE_FIXTURE() :
|
||||
DRC_BASE_FIXTURE()
|
||||
{
|
||||
m_board = std::make_unique<BOARD>();
|
||||
}
|
||||
};
|
||||
|
||||
std::string DRC_BASE_FIXTURE::generate_uuid()
|
||||
{
|
||||
boost::uuids::uuid uuid = boost::uuids::random_generator()();
|
||||
return boost::uuids::to_string( uuid );
|
||||
}
|
||||
|
||||
void DRC_BASE_FIXTURE::loadBoardAndVerifyInitialExclusions( const wxString& aBoardNameStem,
|
||||
int aExpectedInitialExclusions )
|
||||
{
|
||||
KI_TEST::LoadBoard( m_settingsManager, aBoardNameStem, m_board );
|
||||
BOOST_REQUIRE_MESSAGE( m_board,
|
||||
"Could not load board " + aBoardNameStem ); // Ensure board loaded from test data directory
|
||||
PROJECT* pcb_project = m_board->GetProject();
|
||||
BOOST_REQUIRE_MESSAGE( pcb_project, "Get project pointer after initial loading." );
|
||||
|
||||
// Board test file comes with initial exclusions, check if they are preserved after loading
|
||||
const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
size_t initialExclusionsCount = bds.m_DrcExclusions.size();
|
||||
size_t initialExclusionsCommentsCount = bds.m_DrcExclusionComments.size();
|
||||
BOOST_TEST_MESSAGE( "Initial DRC exclusions count: " << initialExclusionsCount );
|
||||
BOOST_CHECK_EQUAL( initialExclusionsCount, (size_t) aExpectedInitialExclusions );
|
||||
BOOST_TEST_MESSAGE( "Initial DRC exclusion comments count: " << initialExclusionsCommentsCount );
|
||||
BOOST_CHECK_EQUAL( initialExclusionsCommentsCount, (size_t) aExpectedInitialExclusions );
|
||||
}
|
||||
|
||||
void DRC_BASE_FIXTURE::createAndVerifyInitialExclusionMarkers()
|
||||
{
|
||||
const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
std::vector<PCB_MARKER*> markers;
|
||||
for( const wxString exclusion : bds.m_DrcExclusions )
|
||||
{
|
||||
PCB_MARKER* marker = PCB_MARKER::DeserializeFromString( exclusion );
|
||||
if( marker )
|
||||
{
|
||||
wxString comment = bds.m_DrcExclusionComments.at( exclusion );
|
||||
marker->SetExcluded( true, comment );
|
||||
markers.push_back( marker );
|
||||
m_board->Add( marker );
|
||||
}
|
||||
}
|
||||
size_t actualExclusionsCount = bds.m_DrcExclusions.size();
|
||||
size_t initialExclusionsCount = markers.size();
|
||||
BOOST_CHECK_EQUAL( actualExclusionsCount, initialExclusionsCount );
|
||||
BOOST_TEST_MESSAGE( std::string( "Actual DRC exclusions count: " ) + std::to_string( actualExclusionsCount )
|
||||
+ " after adding initial markers." );
|
||||
}
|
||||
|
||||
int DRC_BASE_FIXTURE::createAndVerifyAdditionalUnconnectedExclusions( int aAdditionalExclusions,
|
||||
int aInitialExclusions )
|
||||
{
|
||||
for( int i = 0; i < aAdditionalExclusions; ++i )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_UNCONNECTED_ITEMS );
|
||||
wxString id1 = wxString::Format( "12345678-1234-1234-1234-12345678%04d", i );
|
||||
wxString id2 = wxString::Format( "87654321-4321-4321-4321-87654321%04d", i );
|
||||
drcItem->SetItems( KIID( id1 ), KIID( id2 ) );
|
||||
|
||||
PCB_MARKER* marker = new PCB_MARKER( drcItem, VECTOR2I( 1000 * i, 1000 * i ) );
|
||||
m_board->Add( marker );
|
||||
|
||||
// Exclude odd-numbered markers
|
||||
if( i % 2 == 1 )
|
||||
{
|
||||
marker->SetExcluded( true, wxString::Format( "Exclusion %d", i ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Store the new exclusion markers in the board
|
||||
m_board->RecordDRCExclusions();
|
||||
|
||||
// Verify the number of exclusions after adding unconnected items
|
||||
const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
const int expectedExclusions =
|
||||
aInitialExclusions + aAdditionalExclusions / 2; // Only odd-numbered markers are excluded
|
||||
size_t newActualExclusionsCount = bds.m_DrcExclusions.size();
|
||||
BOOST_TEST_MESSAGE( std::string( "New actual DRC exclusions count: " ) + std::to_string( newActualExclusionsCount )
|
||||
+ " after adding unconnected items." );
|
||||
BOOST_CHECK_EQUAL( newActualExclusionsCount, (size_t) expectedExclusions );
|
||||
return expectedExclusions;
|
||||
}
|
||||
|
||||
void DRC_BASE_FIXTURE::runDrcOnBoard()
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Running DRC on board." );
|
||||
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
bds.m_DRCEngine->InitEngine( wxFileName() );
|
||||
bool runDRC = true;
|
||||
bool runDRCOnAllLayers = true;
|
||||
bds.m_DRCEngine->RunTests( EDA_UNITS::MM, runDRC, runDRCOnAllLayers );
|
||||
m_board->RecordDRCExclusions();
|
||||
m_board->ResolveDRCExclusions( false );
|
||||
BOOST_TEST_MESSAGE( "DRC done." );
|
||||
}
|
||||
|
||||
void DRC_BASE_FIXTURE::saveBoardAndProjectToTempFiles( const wxString& aBoardNameStem, FileCleaner& aCleaner,
|
||||
wxString& aTempBoardFullPath, wxString& aTempProjectFullPath,
|
||||
wxString& aTempBoardStemName )
|
||||
{
|
||||
wxString tempPrefix = "tmp_test_drc_";
|
||||
aTempBoardStemName = tempPrefix + aBoardNameStem.ToStdString();
|
||||
aTempBoardFullPath = KI_TEST::GetPcbnewTestDataDir() + aTempBoardStemName + ".kicad_pcb";
|
||||
aCleaner.AddFile( aTempBoardFullPath );
|
||||
wxString tempProjectStemName = tempPrefix + aBoardNameStem.ToStdString();
|
||||
aTempProjectFullPath = KI_TEST::GetPcbnewTestDataDir() + aTempBoardStemName + ".kicad_pro";
|
||||
aCleaner.AddFile( aTempProjectFullPath );
|
||||
|
||||
bool boardSaved = SaveBoardToFile( m_board->GetBoard(), aTempBoardFullPath );
|
||||
BOOST_REQUIRE_MESSAGE( boardSaved, "Save board to temporary file: " << aTempBoardFullPath );
|
||||
|
||||
m_settingsManager.SaveProjectAs( aTempProjectFullPath, m_board->GetProject() );
|
||||
BOOST_REQUIRE_MESSAGE( wxFileName::Exists( aTempProjectFullPath ),
|
||||
"Save project to temporary file: " << aTempProjectFullPath );
|
||||
}
|
||||
|
||||
void DRC_BASE_FIXTURE::reloadBoardAndVerifyExclusions( const wxString& aTempBoardStemName, int aExpectedExclusions )
|
||||
{
|
||||
// clear the current board to ensure a fresh load
|
||||
m_board.reset();
|
||||
|
||||
KI_TEST::LoadBoard( m_settingsManager, aTempBoardStemName, m_board );
|
||||
BOOST_REQUIRE_MESSAGE( m_board, "Could not load board from tempfile:"
|
||||
+ aTempBoardStemName ); // Ensure board loaded from test data directory
|
||||
PROJECT* pcb_project = m_board->GetProject();
|
||||
BOOST_REQUIRE_MESSAGE( pcb_project, "Get project pointer after initial loading." );
|
||||
|
||||
BOARD_DESIGN_SETTINGS& reloaded_bds = m_board->GetDesignSettings();
|
||||
size_t reloadedExclusionsCount = reloaded_bds.m_DrcExclusions.size();
|
||||
BOOST_TEST_MESSAGE( "Reloaded DRC exclusions count: " << reloadedExclusionsCount );
|
||||
BOOST_CHECK_EQUAL( reloadedExclusionsCount, aExpectedExclusions );
|
||||
}
|
||||
|
||||
bool DRC_BASE_FIXTURE::SaveBoardToFile( BOARD* board, const wxString& filename )
|
||||
{
|
||||
try
|
||||
{
|
||||
IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( PCB_IO_MGR::KICAD_SEXP ) );
|
||||
pi->SaveBoard( filename, board, nullptr );
|
||||
return true;
|
||||
}
|
||||
catch( const IO_ERROR& error )
|
||||
{
|
||||
BOOST_TEST_MESSAGE( wxString::Format( "Save board to %s: %s", filename, error.What() ) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
BOOST_FIXTURE_TEST_CASE( DRCUnconnectedExclusionsLoss, DRC_UNCONNECTED_SAVE_FIXTURE )
|
||||
{
|
||||
// Test that unconnected item exclusions are not lost after multiple DRC runs.
|
||||
// This test is expected to fail if the bug (issue17429) is present.
|
||||
|
||||
std::vector<std::pair<wxString, int>> tests = {
|
||||
{ "issue17429", 10 }, // board name stem, expected initial exclusions
|
||||
};
|
||||
|
||||
const int NUM_DRC_RUNS = 2;
|
||||
|
||||
for( const std::pair<wxString, int>& test_params : tests )
|
||||
{
|
||||
wxString boardNameStem = test_params.first;
|
||||
int expectedInitialExclusions = test_params.second;
|
||||
|
||||
loadBoardAndVerifyInitialExclusions( boardNameStem, expectedInitialExclusions );
|
||||
createAndVerifyInitialExclusionMarkers();
|
||||
const int additionalExclusions = 5;
|
||||
int expectedExclusions =
|
||||
createAndVerifyAdditionalUnconnectedExclusions( additionalExclusions, expectedInitialExclusions );
|
||||
|
||||
runDrcOnBoard();
|
||||
|
||||
const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
BOOST_TEST_MESSAGE( std::string( "DRC exclusions count after DRC run: " ) + std::to_string( expectedExclusions )
|
||||
+ " after adding unconnected items." );
|
||||
BOOST_CHECK_EQUAL( bds.m_DrcExclusions.size(), expectedExclusions );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( DRCUnconnectedItemsExclusionsSaveLoad, DRC_REGRESSION_TEST_FIXTURE )
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Test that unconnected item exclusions are not lost during save/load.
|
||||
// This test is expected to fail if the bug (issue17429) is present.
|
||||
|
||||
std::vector<std::pair<wxString, int>> tests = {
|
||||
{ "issue17429", 10 }, // board name stem, expected initial exclusions
|
||||
};
|
||||
|
||||
|
||||
for( const std::pair<wxString, int>& test_params : tests )
|
||||
{
|
||||
FileCleaner tempFileCleaner;
|
||||
wxString boardNameStem = test_params.first;
|
||||
int expectedInitialExclusions = test_params.second;
|
||||
|
||||
loadBoardAndVerifyInitialExclusions( boardNameStem, expectedInitialExclusions );
|
||||
|
||||
wxString tempBoardFullPath, tempProjectFullPath, tempBoardStemName;
|
||||
saveBoardAndProjectToTempFiles( boardNameStem, tempFileCleaner, tempBoardFullPath, tempProjectFullPath,
|
||||
tempBoardStemName );
|
||||
|
||||
createAndVerifyInitialExclusionMarkers();
|
||||
|
||||
const int additionalExclusions = 5;
|
||||
int expectedExclusions =
|
||||
createAndVerifyAdditionalUnconnectedExclusions( additionalExclusions, expectedInitialExclusions );
|
||||
|
||||
bool boardSaved = SaveBoardToFile( m_board->GetBoard(), tempBoardFullPath );
|
||||
BOOST_REQUIRE_MESSAGE( boardSaved, "Save board to temporary file: " << tempBoardFullPath );
|
||||
|
||||
m_settingsManager.SaveProjectAs( tempProjectFullPath, m_board->GetProject() );
|
||||
BOOST_REQUIRE_MESSAGE( wxFileName::Exists( tempProjectFullPath ),
|
||||
"Save project to temporary file: " << tempProjectFullPath );
|
||||
|
||||
reloadBoardAndVerifyExclusions( tempBoardStemName, expectedExclusions );
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user