KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_json_schema_validator.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * 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
20#include <boost/test/unit_test.hpp>
21
24
25#include <atomic>
26
27#if defined( __linux__ ) && defined( __GLIBCXX__ )
28#include <cxxabi.h>
29#include <dlfcn.h>
30#define KI_COUNT_THROWS 1
31#endif
32
33
34namespace
35{
36
37#ifdef KI_COUNT_THROWS
38std::atomic<bool> g_countThrows{ false };
39std::atomic<int> g_throwCount{ 0 };
40#endif
41
42
46class THROW_COUNTER
47{
48public:
49 THROW_COUNTER()
50 {
51#ifdef KI_COUNT_THROWS
52 g_throwCount.store( 0, std::memory_order_relaxed );
53 g_countThrows.store( true, std::memory_order_relaxed );
54#endif
55 }
56
57 ~THROW_COUNTER()
58 {
59#ifdef KI_COUNT_THROWS
60 g_countThrows.store( false, std::memory_order_relaxed );
61#endif
62 }
63
65 int Count() const
66 {
67#ifdef KI_COUNT_THROWS
68 return g_throwCount.load( std::memory_order_relaxed );
69#else
70 return -1;
71#endif
72 }
73
74 static bool Supported()
75 {
76#ifdef KI_COUNT_THROWS
77 return true;
78#else
79 return false;
80#endif
81 }
82};
83
84
85wxFileName schemaPath( const wxString& aName )
86{
87 wxFileName schemaFile = wxFileName::DirName( wxString::FromUTF8( QA_SRC_ROOT ) );
88 schemaFile.AppendDir( wxS( "kicad" ) );
89 schemaFile.AppendDir( wxS( "pcm" ) );
90 schemaFile.AppendDir( wxS( "schemas" ) );
91 schemaFile.SetFullName( aName );
92 return schemaFile;
93}
94
95
96nlohmann::json validPackage()
97{
98 return nlohmann::json{
99 { "name", "Test Package" },
100 { "description", "short description" },
101 { "description_full", "a longer description" },
102 { "identifier", "com.example.test" },
103 { "type", "plugin" },
104 { "author", { { "name", "Someone" },
105 { "contact", { { "web", "https://example.test" } } } } },
106 { "license", "MIT" },
107 { "resources", { { "homepage", "https://example.test" } } },
108 { "versions", nlohmann::json::array( { { { "version", "1.0" },
109 { "status", "stable" },
110 { "kicad_version", "6.0" } } } ) }
111 };
112}
113
114} // namespace
115
116
117#ifdef KI_COUNT_THROWS
118// Interposes the ABI throw entry point, so keep counting scoped to the case that needs it.
119// Defined in __cxxabiv1 to match the <cxxabi.h> declaration exactly. Declaring it at global
120// scope instead collides with the void*-typed forward declaration in <ext/concurrence.h>,
121// which libstdc++ pulls in on one compiler but not the other.
122namespace __cxxabiv1
123{
124extern "C" void __cxa_throw( void* aException, std::type_info* aTypeInfo,
125 void ( *aDestructor )( void* ) )
126{
127 if( g_countThrows.load( std::memory_order_relaxed ) )
128 g_throwCount.fetch_add( 1, std::memory_order_relaxed );
129
130 using THROW_FN = void ( * )( void*, std::type_info*, void ( * )( void* ) );
131 static THROW_FN real = reinterpret_cast<THROW_FN>( dlsym( RTLD_NEXT, "__cxa_throw" ) );
132
133 real( aException, aTypeInfo, aDestructor );
134 __builtin_unreachable();
135}
136} // namespace __cxxabiv1
137#endif
138
139
140BOOST_AUTO_TEST_SUITE( JsonSchemaValidator )
141
142
143
146BOOST_AUTO_TEST_CASE( PcmSchemasLoadWithoutThrowing )
147{
148 for( const wxString& name : { wxS( "pcm.v1.schema.json" ), wxS( "pcm.v2.schema.json" ) } )
149 {
150 wxFileName schema = schemaPath( name );
151 BOOST_REQUIRE_MESSAGE( schema.FileExists(), schema.GetFullPath() );
152
153 THROW_COUNTER counter;
155
156 if( THROW_COUNTER::Supported() )
157 {
158 BOOST_CHECK_MESSAGE( counter.Count() == 0,
159 name << " raised " << counter.Count()
160 << " exception(s) while building the schema" );
161 }
162 }
163}
164
165
169BOOST_AUTO_TEST_CASE( PcmPackageRefsResolve )
170{
171 JSON_SCHEMA_VALIDATOR validator( schemaPath( wxS( "pcm.v1.schema.json" ) ) );
172 nlohmann::json_uri uri( "#/definitions/Package" );
174
175 validator.Validate( validPackage(), good, uri );
176 BOOST_CHECK( !good.HasErrors() );
177
178 // "contact" is required by the Contact definition, which Package only sees through a $ref
179 nlohmann::json bad = validPackage();
180 bad["author"].erase( "contact" );
181
182 COLLECTING_JSON_ERROR_HANDLER missingContact;
183 validator.Validate( bad, missingContact, uri );
184 BOOST_CHECK( missingContact.HasErrors() );
185}
186
187
192BOOST_AUTO_TEST_CASE( BothDefinitionContainersResolve )
193{
194 nlohmann::json schema = nlohmann::json::parse( R"({
195 "$schema": "http://json-schema.org/draft-07/schema#",
196 "$defs": { "Modern": { "type": "integer" } },
197 "definitions": { "Legacy": { "type": "integer" } }
198 })" );
199
200 nlohmann::json_schema::json_validator validator;
201 validator.set_root_schema( schema );
202
203 for( const std::string& fragment : { "#/$defs/Modern", "#/definitions/Legacy" } )
204 {
205 nlohmann::json_uri uri( fragment );
206
208 validator.validate( 42, resolved, uri );
209 BOOST_CHECK_MESSAGE( !resolved.HasErrors(),
210 fragment << " did not resolve: " << resolved.FirstError() );
211
213 validator.validate( "not an integer", enforced, uri );
214 BOOST_CHECK_MESSAGE( enforced.HasErrors(), fragment << " did not enforce its type" );
215 }
216}
217
218
const char * name
Collects JSON-schema validation errors so the caller can inspect them after a validation pass.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
nlohmann::json_schema::json_validator validator
BOOST_AUTO_TEST_CASE(PcmSchemasLoadWithoutThrowing)
CreatePCM() builds these on every project open, where a throw is fatal with the CLR loaded.