KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_constraint_protobuf.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <google/protobuf/any.pb.h>
23
24#include <api/board/board_types.pb.h>
26
27
28BOOST_AUTO_TEST_SUITE( ConstraintSolverProtobuf )
29
30
31// A constraint round-trips through its protobuf Any representation: type, members (uuid + anchor),
32// value and driving flag are all preserved.
34{
36
37 KIID a, b;
39 original.AddMember( b, CONSTRAINT_ANCHOR::END );
40 original.SetValue( 12.5 );
41 original.SetDriving( false );
42
43 google::protobuf::Any container;
44 original.Serialize( container );
45
46 PCB_CONSTRAINT restored( nullptr );
47 BOOST_REQUIRE( restored.Deserialize( container ) );
48
49 BOOST_CHECK( restored.m_Uuid == original.m_Uuid );
50 BOOST_CHECK( restored.GetConstraintType() == PCB_CONSTRAINT_TYPE::FIXED_LENGTH );
51 BOOST_REQUIRE_EQUAL( restored.GetMembers().size(), 2 );
52 BOOST_CHECK( restored.GetMembers()[0].m_item == a );
53 BOOST_CHECK( restored.GetMembers()[0].m_anchor == CONSTRAINT_ANCHOR::START );
54 BOOST_CHECK( restored.GetMembers()[1].m_item == b );
55 BOOST_CHECK( restored.GetMembers()[1].m_anchor == CONSTRAINT_ANCHOR::END );
56 BOOST_CHECK( !restored.IsDriving() );
57 BOOST_REQUIRE( restored.HasValue() );
58 BOOST_CHECK_CLOSE( *restored.GetValue(), 12.5, 1e-9 );
59
60 BOOST_CHECK( restored == original );
61}
62
63
64// A valueless, driving constraint round-trips with no value set.
65BOOST_AUTO_TEST_CASE( RoundTripNoValue )
66{
70
71 google::protobuf::Any container;
72 original.Serialize( container );
73
74 PCB_CONSTRAINT restored( nullptr );
75 BOOST_REQUIRE( restored.Deserialize( container ) );
76
77 BOOST_CHECK( restored.GetConstraintType() == PCB_CONSTRAINT_TYPE::PARALLEL );
78 BOOST_CHECK( !restored.HasValue() );
79 BOOST_CHECK( restored.IsDriving() );
80 BOOST_CHECK( restored == original );
81}
82
83
84// Every constraint type survives the proto round-trip, so a divergence between the C++ enum and
85// the proto ConstraintType (which Serialize bridges by static_cast) fails here in CI instead of
86// silently corrupting API traffic.
87BOOST_AUTO_TEST_CASE( RoundTripEveryType )
88{
89 for( int t = static_cast<int>( PCB_CONSTRAINT_TYPE::COINCIDENT );
90 t <= static_cast<int>( PCB_CONSTRAINT_TYPE::ARC_ANGLE ); ++t )
91 {
92 PCB_CONSTRAINT_TYPE type = static_cast<PCB_CONSTRAINT_TYPE>( t );
93
94 PCB_CONSTRAINT original( nullptr, type );
97
98 google::protobuf::Any container;
99 original.Serialize( container );
100
101 PCB_CONSTRAINT restored( nullptr );
102 BOOST_REQUIRE_MESSAGE( restored.Deserialize( container ),
103 "type " << t << " failed to deserialize" );
104 BOOST_CHECK_MESSAGE( restored.GetConstraintType() == type,
105 "type " << t << " came back as "
106 << static_cast<int>( restored.GetConstraintType() ) );
107 }
108}
109
110
111// VERTEX members keep their index across the round trip non vertex members come back with the
112// negative one sentinel not proto3 scalar default of zero
113BOOST_AUTO_TEST_CASE( RoundTripVertexIndex )
114{
116
117 KIID a, b;
118 original.AddMember( a, CONSTRAINT_ANCHOR::VERTEX, 2 );
119 original.AddMember( b, CONSTRAINT_ANCHOR::START );
120
121 google::protobuf::Any container;
122 original.Serialize( container );
123
124 // Index is presence tracked on the wire so only the vertex member carries it
125 kiapi::board::types::Constraint wire;
126 BOOST_REQUIRE( container.UnpackTo( &wire ) );
127 BOOST_REQUIRE_EQUAL( wire.members_size(), 2 );
128 BOOST_CHECK( wire.members( 0 ).has_index() );
129 BOOST_CHECK( !wire.members( 1 ).has_index() );
130
131 PCB_CONSTRAINT restored( nullptr );
132 BOOST_REQUIRE( restored.Deserialize( container ) );
133
134 BOOST_REQUIRE_EQUAL( restored.GetMembers().size(), 2 );
135 BOOST_CHECK( restored.GetMembers()[0].m_anchor == CONSTRAINT_ANCHOR::VERTEX );
136 BOOST_CHECK_EQUAL( restored.GetMembers()[0].m_index, 2 );
137 BOOST_CHECK( restored.GetMembers()[1].m_anchor == CONSTRAINT_ANCHOR::START );
138 BOOST_CHECK_EQUAL( restored.GetMembers()[1].m_index, -1 );
139
140 BOOST_CHECK( restored == original );
141}
142
143
144// A hand built message with an index on a non vertex anchor must not leak it into the
145// deserialized member only VERTEX anchors may carry one
146BOOST_AUTO_TEST_CASE( DeserializeIgnoresIndexOnNonVertexAnchor )
147{
148 using namespace kiapi::board::types;
149
150 Constraint constraint;
151 constraint.mutable_id()->set_value( KIID().AsStdString() );
152 constraint.set_type( ConstraintType::CT_COINCIDENT );
153 constraint.set_driving( true );
154
155 ConstraintMember* m = constraint.add_members();
156 m->mutable_item()->set_value( KIID().AsStdString() );
157 m->set_anchor( ConstraintAnchor::CA_START );
158 m->set_index( 5 );
159
160 google::protobuf::Any container;
161 container.PackFrom( constraint );
162
163 PCB_CONSTRAINT restored( nullptr );
164 BOOST_REQUIRE( restored.Deserialize( container ) );
165
166 BOOST_REQUIRE_EQUAL( restored.GetMembers().size(), 1 );
167 BOOST_CHECK( restored.GetMembers()[0].m_anchor == CONSTRAINT_ANCHOR::START );
168 BOOST_CHECK_EQUAL( restored.GetMembers()[0].m_index, -1 );
169}
170
171
172// A VERTEX anchor with no index gets the negative one sentinel not a silent vertex zero
173// presence tracking distinguishes this from an explicit index of zero
174BOOST_AUTO_TEST_CASE( DeserializeVertexWithoutIndexGetsSentinel )
175{
176 using namespace kiapi::board::types;
177
178 Constraint constraint;
179 constraint.mutable_id()->set_value( KIID().AsStdString() );
180 constraint.set_type( ConstraintType::CT_COINCIDENT );
181 constraint.set_driving( true );
182
183 ConstraintMember* m = constraint.add_members();
184 m->mutable_item()->set_value( KIID().AsStdString() );
185 m->set_anchor( ConstraintAnchor::CA_VERTEX );
186
187 google::protobuf::Any container;
188 container.PackFrom( constraint );
189
190 PCB_CONSTRAINT restored( nullptr );
191 BOOST_REQUIRE( restored.Deserialize( container ) );
192
193 BOOST_REQUIRE_EQUAL( restored.GetMembers().size(), 1 );
194 BOOST_CHECK( restored.GetMembers()[0].m_anchor == CONSTRAINT_ANCHOR::VERTEX );
195 BOOST_CHECK_EQUAL( restored.GetMembers()[0].m_index, -1 );
196}
197
198
const KIID m_Uuid
Definition eda_item.h:531
Definition kiid.h:44
A geometric constraint between board items (issue #2329).
const std::vector< CONSTRAINT_MEMBER > & GetMembers() const
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
std::optional< double > GetValue() const
bool IsDriving() const
A driving constraint forces its value; a reference (non-driving) one only measures it.
PCB_CONSTRAINT_TYPE GetConstraintType() const
void AddMember(const KIID &aItem, CONSTRAINT_ANCHOR aAnchor=CONSTRAINT_ANCHOR::WHOLE, int aIndex=-1)
bool HasValue() const
void SetValue(std::optional< double > aValue)
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
void SetDriving(bool aDriving)
@ VERTEX
An indexed rectangle corner or polygon outline vertex; pairs with CONSTRAINT_MEMBER::m_index.
@ WHOLE
The item as a whole (a segment as a line, a circle).
@ START
First endpoint of a segment or arc.
@ END
Second endpoint of a segment or arc.
PCB_CONSTRAINT_TYPE
The geometric relationship a PCB_CONSTRAINT enforces between its members.
@ COINCIDENT
Two points are made to coincide.
@ FIXED_LENGTH
A segment has a driving length value.
@ ARC_ANGLE
An arc has a driving or reference swept-angle value.
@ PARALLEL
Two segments are parallel.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_CASE(RoundTrip)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_CHECK_EQUAL(result, "25.4")