KiCad PCB EDA Suite
Loading...
Searching...
No Matches
api_test_utils.h
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 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22#ifndef KICAD_API_TEST_UTILS_H
23#define KICAD_API_TEST_UTILS_H
24
25#include <boost/test/unit_test.hpp>
26#include <boost/bimap.hpp>
27#include <google/protobuf/any.pb.h>
28#include <memory>
29#include <set>
30#include <magic_enum.hpp>
31
32#include <api/api_enums.h>
34
41template <typename KiCadEnum, typename ProtoEnum>
42void testEnums( bool aPartiallyMapped = false )
43{
44 boost::bimap<ProtoEnum, KiCadEnum> protoToKiCadSeen;
45 std::set<ProtoEnum> seenProtos;
46
47 for( ProtoEnum value : magic_enum::enum_values<ProtoEnum>() )
48 {
49 BOOST_TEST_CONTEXT( magic_enum::enum_type_name<ProtoEnum>() << "::" << magic_enum::enum_name( value ) )
50 {
51 std::string name( magic_enum::enum_name( value ) );
52 auto splitPos = name.find_first_of( '_' );
53
54 // Protobuf enum names should be formatted as PREFIX_KEY
55 BOOST_REQUIRE_MESSAGE( splitPos != std::string::npos, "Proto enum name doesn't have a prefix" );
56
57 std::string suffix = name.substr( splitPos );
58
59 // Protobuf enum with the value 0 should not map to anything
60 if( static_cast<int>( value ) == 0 )
61 {
62 BOOST_REQUIRE_MESSAGE( suffix.compare( "_UNKNOWN" ) == 0,
63 "Proto enum with value 0 must be named <PREFIX>_UNKNOWN" );
64 continue;
65 }
66
67 KiCadEnum result;
68 // Every non-unknown Proto value should map to a valid KiCad value
69 BOOST_REQUIRE_NO_THROW( result = ( FromProtoEnum<KiCadEnum, ProtoEnum>( value ) ) );
70
71 // There should be a 1:1 mapping
72 BOOST_REQUIRE( !protoToKiCadSeen.left.count( value ) );
73 protoToKiCadSeen.left.insert( { value, result } );
74 }
75 }
76
77 for( KiCadEnum value : magic_enum::enum_values<KiCadEnum>() )
78 {
79 BOOST_TEST_CONTEXT( magic_enum::enum_type_name<KiCadEnum>() << "::" << magic_enum::enum_name( value ) )
80 {
81 ProtoEnum result;
82
83 if( aPartiallyMapped )
84 {
85 try
86 {
88 }
90 {
91 // If it wasn't mapped from KiCad to Proto, it shouldn't be mapped the other way
92 BOOST_REQUIRE_MESSAGE( !protoToKiCadSeen.right.count( value ),
93 "Proto enum is mapped to this KiCad enum, but not vice versa" );
94 continue;
95 }
96 }
97 else
98 {
99 // Every KiCad enum value should map to a non-unknown Protobuf value
100 BOOST_REQUIRE_NO_THROW( result = ( ToProtoEnum<KiCadEnum, ProtoEnum>( value ) ) );
101 }
102
103 // Protobuf "unknown" should always be zero value by convention
104 BOOST_REQUIRE( result != static_cast<ProtoEnum>( 0 ) );
105
106 // There should be a 1:1 mapping
107 BOOST_REQUIRE( !seenProtos.count( result ) );
108 seenProtos.insert( result );
109
110 // Round-tripping should work
111 KiCadEnum roundTrip = FromProtoEnum<KiCadEnum, ProtoEnum>( result );
112 BOOST_REQUIRE( roundTrip == value );
113 }
114 }
115}
116
117
118template<typename ProtoClass, typename KiCadClass, typename Factory>
119void testProtoFromKiCadObject( KiCadClass* aInput, Factory&& aCreateOutput )
120{
121 BOOST_TEST_CONTEXT( aInput->GetFriendlyName() << ": " << aInput->m_Uuid.AsStdString() )
122 {
123 google::protobuf::Any any;
124 BOOST_REQUIRE_NO_THROW( aInput->Serialize( any ) );
125
126 ProtoClass proto;
127 BOOST_REQUIRE_MESSAGE( any.UnpackTo( &proto ),
128 "Any message did not unpack into the requested type" );
129
130 std::unique_ptr<KiCadClass> output = aCreateOutput();
131
132 bool deserializeResult = false;
133 BOOST_REQUIRE_NO_THROW( deserializeResult = output->Deserialize( any ) );
134 BOOST_REQUIRE_MESSAGE( deserializeResult, "Deserialize failed" );
135
136 google::protobuf::Any outputAny;
137 BOOST_REQUIRE_NO_THROW( output->Serialize( outputAny ) );
138
139 if( !( outputAny.SerializeAsString() == any.SerializeAsString() ) )
140 {
141 BOOST_TEST_MESSAGE( "Input: " << any.Utf8DebugString() );
142 BOOST_TEST_MESSAGE( "Output: " << outputAny.Utf8DebugString() );
143 BOOST_TEST_FAIL( "Round-tripped protobuf does not match" );
144 }
145
146 if( !output->operator==( *aInput ) )
147 BOOST_TEST_FAIL( "Round-tripped object does not match" );
148 }
149}
150
151
152template<typename ProtoClass, typename KiCadClass, typename ParentClass>
153void testProtoFromKiCadObject( KiCadClass* aInput, ParentClass* aParent, bool aStrict = true )
154{
155 if( aStrict )
156 {
158 [aParent]() { return std::make_unique<KiCadClass>( aParent ); } );
159 }
160 else
161 {
163 [aInput]()
164 {
165 std::unique_ptr<KiCadClass> cloned( static_cast<KiCadClass*>( aInput->Clone() ) );
166 return std::make_unique<KiCadClass>( *cloned );
167 } );
168 }
169}
170
171#endif //KICAD_API_TEST_UTILS_H
const char * name
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:44
void testEnums(bool aPartiallyMapped=false)
Checks if a KiCad enum has been properly mapped to a Protobuf enum.
void testProtoFromKiCadObject(KiCadClass *aInput, Factory &&aCreateOutput)
An exception class to represent a WX assertion.
Definition wx_assert.h:47
A type-safe container of any type.
Definition ki_any.h:93
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
nlohmann::json output
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
BOOST_TEST_CONTEXT("Test Clearance")
wxString result
Test unit parsing edge cases and error handling.