Loading files
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
/*******************************************************************************
* Includes
******************************************************************************/
#include <gtest/gtest.h>
#include <librepcb/core/fileio/fileutils.h>
#include <librepcb/core/import/dxfreader.h>
#include <librepcb/core/serialization/sexpression.h>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
namespace tests {
/*******************************************************************************
* Test Class
******************************************************************************/
class DxfReaderTest : public ::testing::Test {
protected:
DxfReader reader; ///< The unit under test
/**
* @brief Helper to call reader.parse() with DXF content as bytearray
*/
void parse(const QByteArray& dxf) {
FilePath fp = FilePath::getRandomTempPath();
FileUtils::writeFile(fp, dxf);
reader.parse(fp);
FileUtils::removeFile(fp);
}
/**
* @brief Helper to easily compare objects as strings for easier debugging
*/
template <typename T>
static std::string str(const T& obj) {
std::unique_ptr<SExpression> node = SExpression::createList("object");
obj.serialize(*node);
return node->toByteArray().toStdString();
}
/**
* @brief Helper to easily compare objects as strings for easier debugging
*/
static std::string str(const DxfReader::Circle& circle) {
std::unique_ptr<SExpression> s = SExpression::createList("object");
circle.position.serialize(s->appendList("position"));
s->appendChild("diameter", circle.diameter);
return s->toByteArray().toStdString();
}
};
/*******************************************************************************
* Test Methods
******************************************************************************/
TEST_F(DxfReaderTest, testInexistentFileThrowsRuntimeError) {
FilePath fp = FilePath::getRandomTempPath();
EXPECT_THROW(reader.parse(fp), RuntimeError);
}
TEST_F(DxfReaderTest, testEmptyFile) {
parse("");
EXPECT_EQ(0, reader.getPoints().count());
EXPECT_EQ(0, reader.getPolygons().count());
EXPECT_EQ(0, reader.getCircles().count());
}
TEST_F(DxfReaderTest, testPointNoUnitIsMillimeters) {
parse(
"0\nSECTION\n"
"2\nENTITIES\n"
"0\nPOINT\n"
"10\n-4.0\n" // X
"20\n-5.0\n" // Y
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(1, reader.getPoints().count());
ASSERT_EQ(0, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Point expected(Length(-4000000), Length(-5000000));
EXPECT_EQ(str(expected), str(reader.getPoints().first()));
}
TEST_F(DxfReaderTest, testPointUnspecifiedUnitIsMillimeters) {
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n0\n" // UNIT = unspecified
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nPOINT\n"
"10\n-4.0\n" // X
"20\n-5.0\n" // Y
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(1, reader.getPoints().count());
ASSERT_EQ(0, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Point expected(Length(-4000000), Length(-5000000));
EXPECT_EQ(str(expected), str(reader.getPoints().first()));
}
TEST_F(DxfReaderTest, testPointMillimeters) {
reader.setScaleFactor(2);
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n4\n" // UNIT = millimeters
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nPOINT\n"
"10\n-4.0\n" // X
"20\n-5.0\n" // Y
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(1, reader.getPoints().count());
ASSERT_EQ(0, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Point expected(Length(-8000000), Length(-10000000));
EXPECT_EQ(str(expected), str(reader.getPoints().first()));
}
TEST_F(DxfReaderTest, testPointInches) {
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n1\n" // UNIT = inches
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nPOINT\n"
"10\n-4.0\n" // X
"20\n-5.0\n" // Y
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(1, reader.getPoints().count());
ASSERT_EQ(0, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Point expected(Length(-101600000), Length(-127000000));
EXPECT_EQ(str(expected), str(reader.getPoints().first()));
}
TEST_F(DxfReaderTest, testCircle) {
reader.setScaleFactor(2);
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nCIRCLE\n"
"10\n4.0\n" // CX
"20\n5.0\n" // CY
"40\n8.0\n" // RADIUS
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(0, reader.getPolygons().count());
ASSERT_EQ(1, reader.getCircles().count());
DxfReader::Circle expected{Point(Length(8000), Length(10000)),
PositiveLength(32000)}; // diameter, not radius!
EXPECT_EQ(str(expected), str(reader.getCircles().first()));
}
TEST_F(DxfReaderTest, testLine) {
reader.setScaleFactor(2);
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nLINE\n"
"10\n4.0\n" // X1
"20\n5.0\n" // Y1
"11\n8.0\n" // X2
"21\n10.0\n" // Y2
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(1, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Path expected({
Vertex(Point(Length(8000), Length(10000)), Angle(0)),
Vertex(Point(Length(16000), Length(20000)), Angle(0)),
});
EXPECT_EQ(str(expected), str(reader.getPolygons().first()));
}
TEST_F(DxfReaderTest, testArcFrom90To180Deg) {
reader.setScaleFactor(2);
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nARC\n"
"10\n4.0\n" // CX
"20\n5.0\n" // CY
"40\n2.0\n" // RADIUS
"50\n90.0\n" // START ANGLE
"51\n180.0\n" // END ANGLE
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(1, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Path expected({
Vertex(Point(Length(8000), Length(14000)), Angle::deg90()),
Vertex(Point(Length(4000), Length(10000)), Angle(0)),
});
EXPECT_EQ(str(expected), str(reader.getPolygons().first()));
}
TEST_F(DxfReaderTest, testArcFrom180To90Deg) {
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nARC\n"
"10\n4.0\n" // CX
"20\n5.0\n" // CY
"40\n2.0\n" // RADIUS
"50\n180.0\n" // START ANGLE
"51\n90.0\n" // END ANGLE
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(1, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Path expected({
Vertex(Point(Length(2000), Length(5000)), Angle::deg270()),
Vertex(Point(Length(4000), Length(7000)), Angle(0)),
});
EXPECT_EQ(str(expected), str(reader.getPolygons().first()));
}
TEST_F(DxfReaderTest, testArcFromMinus90To90Deg) {
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nARC\n"
"10\n4.0\n" // CX
"20\n5.0\n" // CY
"40\n2.0\n" // RADIUS
"50\n-90.0\n" // START ANGLE
"51\n90.0\n" // END ANGLE
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(1, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Path expected({
Vertex(Point(Length(4000), Length(3000)), Angle::deg180()),
Vertex(Point(Length(4000), Length(7000)), Angle(0)),
});
EXPECT_EQ(str(expected), str(reader.getPolygons().first()));
}
TEST_F(DxfReaderTest, testArcFrom90ToMinus90Deg) {
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nARC\n"
"10\n4.0\n" // CX
"20\n5.0\n" // CY
"40\n2.0\n" // RADIUS
"50\n90.0\n" // START ANGLE
"51\n-90.0\n" // END ANGLE
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(1, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Path expected({
Vertex(Point(Length(4000), Length(7000)), Angle::deg180()),
Vertex(Point(Length(4000), Length(3000)), Angle(0)),
});
EXPECT_EQ(str(expected), str(reader.getPolygons().first()));
}
TEST_F(DxfReaderTest, testLwPolylineBulgeCcw) {
reader.setScaleFactor(2);
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nLWPOLYLINE\n"
"90\n2\n" // NUMBER OF VERTICES
"70\n0\n" // FLAGS (0=open, 1=closed)
"10\n4.0\n" // X1
"20\n5.0\n" // Y1
"42\n1.0\n" // BULGE
"10\n6.0\n" // X2
"20\n5.0\n" // Y2
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(1, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Path expected({
Vertex(Point(Length(8000), Length(10000)), Angle::deg180()),
Vertex(Point(Length(12000), Length(10000)), Angle(0)),
});
EXPECT_EQ(str(expected), str(reader.getPolygons().first()));
}
// Polyline as used in DXF R12 format.
TEST_F(DxfReaderTest, testPolyline) {
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nPOLYLINE\n"
"70\n0\n" // FLAGS (0=open, 1=closed)
"0\nVERTEX\n"
"10\n4.0\n" // X1
"20\n5.0\n" // Y1
"0\nVERTEX\n"
"10\n4.0\n" // X2
"20\n7.0\n" // Y2
"42\n1.0\n" // BULGE
"0\nVERTEX\n"
"10\n6.0\n" // X3
"20\n7.0\n" // Y3
"0\nVERTEX\n"
"10\n6.0\n" // X4
"20\n5.0\n" // Y4
"0\nSEQEND\n"
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(1, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Path expected({
Vertex(Point(Length(4000), Length(5000)), Angle(0)),
Vertex(Point(Length(4000), Length(7000)), Angle::deg180()),
Vertex(Point(Length(6000), Length(7000)), Angle(0)),
Vertex(Point(Length(6000), Length(5000)), Angle(0)),
});
EXPECT_EQ(str(expected), str(reader.getPolygons().first()));
}
// Polyline as used in recent DXF formats.
TEST_F(DxfReaderTest, testLwPolyline) {
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nLWPOLYLINE\n"
"90\n4\n" // NUMBER OF VERTICES
"70\n0\n" // FLAGS (0=open, 1=closed)
"10\n4.0\n" // X1
"20\n5.0\n" // Y1
"10\n4.0\n" // X2
"20\n7.0\n" // Y2
"42\n1.0\n" // BULGE
"10\n6.0\n" // X3
"20\n7.0\n" // Y3
"10\n6.0\n" // X4
"20\n5.0\n" // Y4
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(1, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Path expected({
Vertex(Point(Length(4000), Length(5000)), Angle(0)),
Vertex(Point(Length(4000), Length(7000)), Angle::deg180()),
Vertex(Point(Length(6000), Length(7000)), Angle(0)),
Vertex(Point(Length(6000), Length(5000)), Angle(0)),
});
EXPECT_EQ(str(expected), str(reader.getPolygons().first()));
}
TEST_F(DxfReaderTest, testLwPolylineBulgeCw) {
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nLWPOLYLINE\n"
"90\n2\n" // NUMBER OF VERTICES
"70\n0\n" // FLAGS (0=open, 1=closed)
"10\n4.0\n" // X1
"20\n5.0\n" // Y1
"42\n-1.0\n" // BULGE
"10\n6.0\n" // X2
"20\n5.0\n" // Y2
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(1, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Path expected({
Vertex(Point(Length(4000), Length(5000)), -Angle::deg180()),
Vertex(Point(Length(6000), Length(5000)), Angle(0)),
});
EXPECT_EQ(str(expected), str(reader.getPolygons().first()));
}
TEST_F(DxfReaderTest, testLwPolylineClosed) {
parse(
"0\nSECTION\n"
"2\nHEADER\n"
"9\n$INSUNITS\n"
"70\n13\n" // UNIT = micrometers
"0\nENDSEC\n"
"2\nENTITIES\n"
"0\nLWPOLYLINE\n"
"90\n3\n" // NUMBER OF VERTICES
"70\n1\n" // FLAGS (0=open, 1=closed)
"10\n4.0\n" // X1
"20\n5.0\n" // Y1
"10\n4.0\n" // X2
"20\n7.0\n" // Y2
"10\n6.0\n" // X3
"20\n7.0\n" // Y3
"0\nENDSEC\n"
"0\nEOF\n");
// Assert(!) for number of elements to avoid illegal list item access below.
ASSERT_EQ(0, reader.getPoints().count());
ASSERT_EQ(1, reader.getPolygons().count());
ASSERT_EQ(0, reader.getCircles().count());
Path expected({
Vertex(Point(Length(4000), Length(5000)), Angle(0)),
Vertex(Point(Length(4000), Length(7000)), Angle(0)),
Vertex(Point(Length(6000), Length(7000)), Angle(0)),
Vertex(Point(Length(4000), Length(5000)), Angle(0)),
});
EXPECT_EQ(str(expected), str(reader.getPolygons().first()));
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace tests
} // namespace librepcb
94ec4b8feThis file is pinned to the selected release. Download the original to open it in your design tool.