KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pads_sch_sdb.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 option)
9 * 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 along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
22
24#include <sch_io/ole_image.h>
25
26#include <wx/image.h>
27
28#include <json_common.h>
29
30#include <algorithm>
31#include <fstream>
32#include <iterator>
33#include <stdexcept>
34#include <string>
35#include <vector>
36
39
40namespace
41{
42
43static std::vector<uint8_t> loadBinary( const std::string& aPath )
44{
45 std::ifstream file( aPath, std::ios::binary );
46
47 if( !file )
48 throw std::runtime_error( "Cannot open test fixture: " + aPath );
49
50 return { std::istreambuf_iterator<char>( file ), std::istreambuf_iterator<char>() };
51}
52
53
54static std::vector<uint8_t> loadPublicFixture()
55{
56 return loadBinary( KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/binary/minimal_v13.sch" );
57}
58
59
60static void putU32( std::vector<uint8_t>& aBytes, size_t aOffset, uint32_t aValue )
61{
62 aBytes.at( aOffset ) = static_cast<uint8_t>( aValue );
63 aBytes.at( aOffset + 1 ) = static_cast<uint8_t>( aValue >> 8 );
64 aBytes.at( aOffset + 2 ) = static_cast<uint8_t>( aValue >> 16 );
65 aBytes.at( aOffset + 3 ) = static_cast<uint8_t>( aValue >> 24 );
66}
67
68
69static bool errorContains( const IO_ERROR& aError, const std::string& aNeedle1, const std::string& aNeedle2 )
70{
71 std::string message = aError.What().ToStdString();
72 return message.find( aNeedle1 ) != std::string::npos && message.find( aNeedle2 ) != std::string::npos;
73}
74
75
76static void checkPhysicalPartition( const std::string& aPath )
77{
78 PADS_SCH_SDB sdb;
79 sdb.Load( loadBinary( aPath ) );
80
81 const auto& blocks = sdb.Blocks();
82 BOOST_REQUIRE_MESSAGE( !blocks.empty(), aPath );
83 BOOST_CHECK_EQUAL( blocks.front().offset, sdb.PayloadOffset() );
84
85 for( size_t i = 1; i < blocks.size(); ++i )
86 BOOST_CHECK_EQUAL( blocks[i - 1].End(), blocks[i].offset );
87
88 BOOST_CHECK_EQUAL( blocks.back().End(), sdb.FooterOffset() );
89}
90
91} // namespace
92
93
94BOOST_AUTO_TEST_SUITE( PadsSchBinarySdb )
95
96
97BOOST_AUTO_TEST_CASE( PublicV13Container )
98{
99 std::vector<uint8_t> bytes = loadPublicFixture();
100 PADS_SCH_SDB sdb;
101 sdb.Load( bytes );
102
103 BOOST_CHECK( PADS_SCH_SDB::HasFamilyMagic( bytes ) );
104 BOOST_CHECK( PADS_SCH_SDB::IsSupportedVersion( 0x000C ) );
105 BOOST_CHECK( PADS_SCH_SDB::IsSupportedVersion( 0x000D ) );
106 BOOST_CHECK( !PADS_SCH_SDB::IsSupportedVersion( 0x1234 ) );
107 BOOST_CHECK_EQUAL( sdb.Version(), 0x000D );
108 BOOST_CHECK_EQUAL( sdb.Pools().size(), 20 );
109 BOOST_CHECK_EQUAL( sdb.PayloadOffset(), 0x250 );
110 BOOST_CHECK_LT( sdb.FooterOffset(), sdb.Bytes().size() );
111 BOOST_CHECK_EQUAL( sdb.Cursor().Size(), sdb.Bytes().size() );
112
113 BOOST_CHECK_EQUAL( sdb.Pools()[0].allocatedBytes, 588 );
114 BOOST_CHECK_EQUAL( sdb.Pools()[0].count, 21 );
115 BOOST_CHECK_EQUAL( sdb.Pools()[0].usedBytes, 588 );
116 BOOST_CHECK_EQUAL( sdb.Pools()[0].handle, 140233272 );
117 BOOST_CHECK_EQUAL( sdb.Pools()[3].allocatedBytes, 49152 );
118 BOOST_CHECK_EQUAL( sdb.Pools()[3].count, 1 );
119 BOOST_CHECK_EQUAL( sdb.Pools()[3].usedBytes, 48 );
120 BOOST_CHECK_EQUAL( sdb.Pools()[3].handle, 140233872 );
121 BOOST_CHECK_EQUAL( sdb.Pools()[9].allocatedBytes, 1280 );
122 BOOST_CHECK_EQUAL( sdb.Pools()[9].count, 0 );
123 BOOST_CHECK_EQUAL( sdb.Pools()[9].usedBytes, 0 );
124 BOOST_CHECK_EQUAL( sdb.Pools()[9].handle, 179219184 );
125}
126
127
128BOOST_AUTO_TEST_CASE( PhysicalStreamPartitionsCorpus )
129{
130 const std::string publicRoot = KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/binary/";
131 std::ifstream publicFile( publicRoot + "manifest.json" );
132 nlohmann::json publicManifest;
133 publicFile >> publicManifest;
134
135 for( const nlohmann::json& fixture : publicManifest.at( "fixtures" ) )
136 checkPhysicalPartition( publicRoot + fixture.at( "binary" ).get<std::string>() );
137
138}
139
140
141BOOST_AUTO_TEST_CASE( MinimalV13PhysicalLedger )
142{
143 PADS_SCH_SDB sdb;
144 sdb.Load( loadPublicFixture() );
145 const auto& blocks = sdb.Blocks();
146
147 BOOST_REQUIRE_EQUAL( blocks.size(), 3 );
148 BOOST_CHECK( blocks[0].kind == SCH_SDB_BLOCK_KIND::FIXED_CONTROLLER );
149 BOOST_CHECK_EQUAL( blocks[0].controller, 0x24 );
150 BOOST_CHECK_EQUAL( blocks[0].offset, 0x250 );
151 BOOST_CHECK_EQUAL( blocks[0].End(), 0x5439 );
152 BOOST_CHECK_EQUAL( blocks[0].count, 19 );
153 BOOST_CHECK_EQUAL( blocks[0].stride, 0 );
154 BOOST_CHECK( blocks[1].kind == SCH_SDB_BLOCK_KIND::SHEET );
155 BOOST_CHECK_EQUAL( blocks[1].offset, 0x5439 );
156 BOOST_CHECK_EQUAL( blocks[1].End(), 0x9938 );
157 BOOST_CHECK( blocks[2].kind == SCH_SDB_BLOCK_KIND::FOOTER_AUX );
158 BOOST_CHECK_EQUAL( blocks[2].offset, 0x9938 );
159 BOOST_CHECK_EQUAL( blocks[2].bytes, 4 );
160}
161
162
163BOOST_AUTO_TEST_CASE( EmbeddedOleObjects )
164{
165 PADS_SCH_SDB sdb;
166 sdb.Load( loadBinary( KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/binary/ole_images.sch" ) );
167
168 const auto isContainerItem = []( const PADS_SCH_BINARY::SCH_SDB_BLOCK& aBlock )
169 {
170 return aBlock.kind == SCH_SDB_BLOCK_KIND::CFB_CONTAINER_ITEM;
171 };
172
173 const auto& blocks = sdb.Blocks();
174 std::vector<const PADS_SCH_BINARY::SCH_SDB_BLOCK*> items;
175
176 for( const auto& block : blocks )
177 {
178 if( isContainerItem( block ) )
179 items.push_back( &block );
180 }
181
182 BOOST_REQUIRE_EQUAL( items.size(), 2 );
183 BOOST_REQUIRE_EQUAL( sdb.OleItems().size(), 2 );
184 BOOST_CHECK( ( sdb.OleItems()[0].extent == std::array<int32_t, 4>{ 30, 30, 350, 210 } ) );
185 BOOST_CHECK_EQUAL( sdb.OleItems()[0].left, -15766 );
186 BOOST_CHECK_EQUAL( sdb.OleItems()[0].bottom, -10366 );
187 BOOST_CHECK_EQUAL( sdb.OleItems()[0].right, -13296 );
188 BOOST_CHECK_EQUAL( sdb.OleItems()[0].top, -11755 );
189 BOOST_CHECK_EQUAL( sdb.OleItems()[0].sheetPlane, 0 );
190 BOOST_CHECK_EQUAL( sdb.OleItems()[0].flags, 1 );
191 BOOST_CHECK( ( sdb.OleItems()[1].extent == std::array<int32_t, 4>{ 30, 30, 170, 84 } ) );
192 BOOST_CHECK_EQUAL( sdb.OleItems()[1].left, -15766 );
193 BOOST_CHECK_EQUAL( sdb.OleItems()[1].bottom, -10366 );
194 BOOST_CHECK_EQUAL( sdb.OleItems()[1].right, -14686 );
195 BOOST_CHECK_EQUAL( sdb.OleItems()[1].top, -10782 );
196 BOOST_CHECK_EQUAL( sdb.OleItems()[1].sheetPlane, 0 );
197 BOOST_CHECK_EQUAL( sdb.OleItems()[1].flags, 1 );
198
199 OLE_IMAGE_PAYLOAD bitmap = ExtractOleImage( sdb.Bytes().data() + items[0]->offset, items[0]->bytes );
200 BOOST_CHECK( bitmap.type == OLE_IMAGE_TYPE::BMP );
201 BOOST_CHECK_EQUAL( bitmap.streamName, "\\x01Ole10Native" );
202 BOOST_REQUIRE_GE( bitmap.data.size(), 2 );
203 BOOST_CHECK_EQUAL( bitmap.data[0], 'B' );
204 BOOST_CHECK_EQUAL( bitmap.data[1], 'M' );
205
206 OLE_IMAGE_PAYLOAD metafile = ExtractOleImage( sdb.Bytes().data() + items[1]->offset, items[1]->bytes );
207 BOOST_CHECK( metafile.type == OLE_IMAGE_TYPE::WMF );
208 BOOST_CHECK_EQUAL( metafile.streamName, "\\x01Ole10Native" );
209 BOOST_REQUIRE_GE( metafile.data.size(), 4 );
210 BOOST_CHECK_EQUAL( metafile.data[0], 0xD7 );
211 BOOST_CHECK_EQUAL( metafile.data[1], 0xCD );
212 BOOST_CHECK_EQUAL( metafile.data[2], 0xC6 );
213 BOOST_CHECK_EQUAL( metafile.data[3], 0x9A );
214 wxImage rendered;
215 BOOST_REQUIRE( OleRenderWmf( metafile.data, 1024, 1024, rendered ) );
216 BOOST_CHECK( rendered.IsOk() );
217 BOOST_CHECK_GT( rendered.GetWidth(), 0 );
218 BOOST_CHECK_GT( rendered.GetHeight(), 0 );
219}
220
221
222BOOST_AUTO_TEST_CASE( RejectsEmbeddedOleWrongSheet )
223{
224 std::vector<uint8_t> bytes =
225 loadBinary( KI_TEST::GetEeschemaTestDataDir() + "/plugins/pads/binary/ole_images.sch" );
226 PADS_SCH_SDB parsed;
227 parsed.Load( bytes );
228 BOOST_REQUIRE_EQUAL( parsed.OleItems().size(), 2 );
229 size_t sheetOffset = parsed.OleItems()[0].trailerOffset + 38 + 16 + 16;
230 putU32( bytes, sheetOffset, 1 );
231
232 PADS_SCH_SDB sdb;
233 auto hasOffset = [sheetOffset]( const IO_ERROR& e )
234 {
235 return errorContains( e, "v0x000D", wxString::Format( "0x%zX", sheetOffset ).ToStdString() );
236 };
237 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR, hasOffset );
238}
239
240
241BOOST_AUTO_TEST_CASE( RejectsShortHeader )
242{
243 std::vector<uint8_t> bytes = loadPublicFixture();
244 bytes.resize( 31 );
245
246 PADS_SCH_SDB sdb;
247 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
248 []( const IO_ERROR& e )
249 {
250 return errorContains( e, "v0x000D", "0x1F" );
251 } );
252}
253
254
255BOOST_AUTO_TEST_CASE( RejectsBadMagic )
256{
257 std::vector<uint8_t> bytes = loadPublicFixture();
258 bytes[1] ^= 1;
259
260 PADS_SCH_SDB sdb;
261 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
262 []( const IO_ERROR& e )
263 {
264 return errorContains( e, "v0x000D", "0x1" );
265 } );
266}
267
268
269BOOST_AUTO_TEST_CASE( RejectsUnsupportedVersion )
270{
271 std::vector<uint8_t> bytes = loadPublicFixture();
272 bytes[2] = 0x34;
273 bytes[3] = 0x12;
274
275 PADS_SCH_SDB sdb;
276 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
277 []( const IO_ERROR& e )
278 {
279 return errorContains( e, "v0x1234", "0x2" );
280 } );
281}
282
283
284BOOST_AUTO_TEST_CASE( RejectsBadV13FormatFlags )
285{
286 std::vector<uint8_t> bytes = loadPublicFixture();
287 bytes[4] = 1;
288
289 PADS_SCH_SDB sdb;
290 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
291 []( const IO_ERROR& e )
292 {
293 return errorContains( e, "v0x000D", "0x4" );
294 } );
295}
296
297
298BOOST_AUTO_TEST_CASE( RejectsBadV12FormatFlags )
299{
300 std::vector<uint8_t> bytes = loadPublicFixture();
301 bytes[2] = 0x0C;
302 bytes[4] = 0;
303
304 PADS_SCH_SDB sdb;
305 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
306 []( const IO_ERROR& e )
307 {
308 return errorContains( e, "v0x000C", "0x4" );
309 } );
310}
311
312
313BOOST_AUTO_TEST_CASE( RejectsBadFooterGuid )
314{
315 std::vector<uint8_t> bytes = loadPublicFixture();
316 size_t footer = bytes.size() - 42;
317 bytes[footer] ^= 1;
318
319 PADS_SCH_SDB sdb;
320 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
321 [footer]( const IO_ERROR& e )
322 {
323 return errorContains( e, "v0x000D", wxString::Format( "0x%zX", footer ).ToStdString() );
324 } );
325}
326
327
328BOOST_AUTO_TEST_CASE( RejectsBadFooterBackPointer )
329{
330 std::vector<uint8_t> bytes = loadPublicFixture();
331 size_t pointerOffset = bytes.size() - 4;
332 putU32( bytes, pointerOffset, static_cast<uint32_t>( bytes.size() + 1 ) );
333
334 PADS_SCH_SDB sdb;
335 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
336 [pointerOffset]( const IO_ERROR& e )
337 {
338 return errorContains( e, "v0x000D",
339 wxString::Format( "0x%zX", pointerOffset ).ToStdString() );
340 } );
341}
342
343
344BOOST_AUTO_TEST_CASE( RejectsUsedBytesAboveAllocation )
345{
346 std::vector<uint8_t> bytes = loadPublicFixture();
347 constexpr size_t usedBytesOffset = 0x20 + 12;
348 putU32( bytes, usedBytesOffset, 589 );
349
350 PADS_SCH_SDB sdb;
351 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
352 []( const IO_ERROR& e )
353 {
354 return errorContains( e, "v0x000D", "0x2C" );
355 } );
356}
357
358
359BOOST_AUTO_TEST_CASE( RejectsUsedBytesForEmptyPool )
360{
361 std::vector<uint8_t> bytes = loadPublicFixture();
362 constexpr size_t descriptor = 0x20 + 9 * 28;
363 putU32( bytes, descriptor + 12, 1 );
364
365 PADS_SCH_SDB sdb;
366 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
367 []( const IO_ERROR& e )
368 {
369 return errorContains( e, "v0x000D", "0x128" );
370 } );
371}
372
373
374BOOST_AUTO_TEST_CASE( RejectsOuterControllerPastFooter )
375{
376 std::vector<uint8_t> bytes = loadPublicFixture();
377 constexpr size_t descriptor = 0x20 + 1 * 28;
378 putU32( bytes, descriptor + 4, UINT32_MAX );
379 putU32( bytes, descriptor + 12, UINT32_MAX );
380
381 PADS_SCH_SDB sdb;
382 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
383 []( const IO_ERROR& e )
384 {
385 return errorContains( e, "v0x000D", "0x48" );
386 } );
387}
388
389
390BOOST_AUTO_TEST_CASE( RejectsSheetControllerPastFooter )
391{
392 std::vector<uint8_t> bytes = loadPublicFixture();
393 constexpr size_t descriptor = 0x5439 + 20;
394 putU32( bytes, descriptor + 8, UINT32_MAX );
395 putU32( bytes, descriptor + 16, UINT32_MAX );
396
397 PADS_SCH_SDB sdb;
398 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
399 []( const IO_ERROR& e )
400 {
401 return errorContains( e, "v0x000D", "0x545D" );
402 } );
403}
404
405
406BOOST_AUTO_TEST_CASE( RejectsDerivedSheetCountPastFooter )
407{
408 std::vector<uint8_t> bytes = loadPublicFixture();
409 constexpr size_t countOffset = 0x20 + 3 * 28 + 8;
410 putU32( bytes, countOffset, UINT32_MAX );
411
412 PADS_SCH_SDB sdb;
413 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
414 []( const IO_ERROR& e )
415 {
416 return errorContains( e, "v0x000D", "0x7C" );
417 } );
418}
419
420
421BOOST_AUTO_TEST_CASE( RejectsHugePreviewCountExtent )
422{
423 std::vector<uint8_t> bytes = loadPublicFixture();
424 constexpr size_t itemCountOffset = 0x9938;
425 putU32( bytes, itemCountOffset, UINT32_MAX );
426
427 PADS_SCH_SDB sdb;
428 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
429 []( const IO_ERROR& e )
430 {
431 return errorContains( e, "v0x000D", "0x9938" );
432 } );
433}
434
435
436BOOST_AUTO_TEST_CASE( RejectsFooterAuxBaseBeforePayload )
437{
438 std::vector<uint8_t> bytes = loadPublicFixture();
439 size_t pointerOffset = bytes.size() - 4;
440 putU32( bytes, pointerOffset, 0x24F );
441
442 PADS_SCH_SDB sdb;
443 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
444 [pointerOffset]( const IO_ERROR& e )
445 {
446 return errorContains( e, "v0x000D",
447 wxString::Format( "0x%zX", pointerOffset ).ToStdString() );
448 } );
449}
450
451
452BOOST_AUTO_TEST_CASE( RejectsFooterAuxOverlapWithDerivedBlocks )
453{
454 std::vector<uint8_t> bytes = loadPublicFixture();
455 size_t pointerOffset = bytes.size() - 4;
456 putU32( bytes, pointerOffset, 0x250 );
457
458 PADS_SCH_SDB sdb;
459 BOOST_CHECK_EXCEPTION( sdb.Load( std::move( bytes ) ), IO_ERROR,
460 [pointerOffset]( const IO_ERROR& e )
461 {
462 return errorContains( e, "v0x000D",
463 wxString::Format( "0x%zX", pointerOffset ).ToStdString() );
464 } );
465}
466
467
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
const std::array< SCH_SDB_POOL, 20 > & Pools() const
const PADS_IO::BINARY_CURSOR & Cursor() const
void Load(std::vector< uint8_t > aBytes)
const std::vector< uint8_t > & Bytes() const
const std::vector< SCH_SDB_BLOCK > & Blocks() const
const std::vector< SCH_SDB_OLE_ITEM > & OleItems() const
static bool IsSupportedVersion(uint16_t aVersion)
static bool HasFamilyMagic(const std::vector< uint8_t > &aBytes)
static std::string ToStdString(const wxString &aStr)
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
bool OleRenderWmf(const std::vector< uint8_t > &aWmf, int aMaxWidth, int aMaxHeight, wxImage &aImage, double aTargetAspect)
OLE_IMAGE_PAYLOAD ExtractOleImage(const uint8_t *aCfb, size_t aSize)
Prefer CONTENTS, then OlePres000, then the native stream.
std::vector< uint8_t > data
Definition ole_image.h:48
std::string streamName
Definition ole_image.h:49
OLE_IMAGE_TYPE type
Definition ole_image.h:47
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(PublicV13Container)
BOOST_CHECK_EQUAL(result, "25.4")