mirror of https://github.com/KLayout/klayout.git
DRC tests integrated now + refactoring
Refactoring: * more consistent behavior of output redirection * ut library was refactored and split into several .h/.cc * concept of long runners introduced * long runners can be skipped with "test_is_long_runner()" * same for editable/non-editable mode tests
This commit is contained in:
parent
e5c6bd9e29
commit
be1b007f2f
|
|
@ -4181,14 +4181,14 @@ CODE
|
|||
@output_layout_file = nil
|
||||
else
|
||||
@output_layout = RBA::Layout::new
|
||||
@output_cell = @output_layout.create_cell(cellname.to_s || "TOP")
|
||||
@output_cell = cellname && @output_layout.create_cell(cellname.to_s)
|
||||
@output_layout_file = arg
|
||||
end
|
||||
|
||||
elsif arg.is_a?(RBA::Layout)
|
||||
|
||||
@output_layout = arg
|
||||
@output_cell = @output_layout.create_cell(cellname.to_s || "TOP")
|
||||
@output_cell = cellname && (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s))
|
||||
@output_layout_file = nil
|
||||
|
||||
elsif arg.is_a?(RBA::Cell)
|
||||
|
|
|
|||
|
|
@ -63,3 +63,37 @@ TEST(1)
|
|||
this->compare_layouts (layout, au, ut::NoNormalization);
|
||||
}
|
||||
|
||||
TEST(2)
|
||||
{
|
||||
lym::Macro drc;
|
||||
drc.set_text (
|
||||
"dbu 0.001\n"
|
||||
"def compare(a, b, ex)\n"
|
||||
" a = a.to_s\n"
|
||||
" b = b.to_s\n"
|
||||
" if a != b\n"
|
||||
" raise(ex + \" (actual=#{a}, ref=#{b})\")\n"
|
||||
" end\n"
|
||||
"end\n"
|
||||
"compare(0.1.um, 0.1, \"unexpected value when converting um\")\n"
|
||||
"compare(0.1.micron, 0.1, \"unexpected value when converting micron\")\n"
|
||||
"compare(0.1.um2, 0.1, \"unexpected value when converting um2\")\n"
|
||||
"compare(0.1.mm2, 100000.0, \"unexpected value when converting mm2\")\n"
|
||||
"compare(120.dbu, 0.12, \"unexpected value when converting dbu\")\n"
|
||||
"compare((0.1.um + 120.dbu), 0.22, \"unexpected value when adding values\")\n"
|
||||
"compare(0.1.mm, 100.0, \"unexpected value when converting mm\")\n"
|
||||
"compare(1e-6.m, 1.0, \"unexpected value when converting m\")\n"
|
||||
"compare(1.um, 1.0, \"unexpected value when converting integer um\")\n"
|
||||
"compare(1.micron, 1.0, \"unexpected value when convering integer micron\")\n"
|
||||
"compare(1.um2, 1.0, \"unexpected value when converting integer um2\")\n"
|
||||
"compare(1.mm2, 1000000.0, \"unexpected value when converting integer mm2\")\n"
|
||||
"compare((1.um + 120.dbu), 1.12, \"unexpected value when adding integer values\")\n"
|
||||
"compare(1.mm, 1000.0, \"unexpected value when converting integer mm\")\n"
|
||||
"compare(1.m, 1000000.0, \"unexpected value when converting integer m\")\n"
|
||||
);
|
||||
drc.set_interpreter (lym::Macro::DSLInterpreter);
|
||||
drc.set_dsl_interpreter ("drc-dsl");
|
||||
|
||||
EXPECT_EQ (drc.run (), 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,142 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "utHead.h"
|
||||
#include "dbReader.h"
|
||||
#include "lymMacro.h"
|
||||
|
||||
TEST(1)
|
||||
{
|
||||
std::string rs = ut::testsrc ();
|
||||
rs += "/testdata/drc/drcSimpleTests_1.drc";
|
||||
|
||||
std::string au = ut::testsrc ();
|
||||
au += "/testdata/drc/drcSimpleTests_au1.gds";
|
||||
|
||||
std::string output = this->tmp_file ("tmp.gds");
|
||||
|
||||
{
|
||||
// Set some variables
|
||||
lym::Macro config;
|
||||
config.set_text (tl::sprintf (
|
||||
"$drc_test_source = nil\n"
|
||||
"$drc_test_target = \"%s\"\n"
|
||||
, output)
|
||||
);
|
||||
config.set_interpreter (lym::Macro::Ruby);
|
||||
EXPECT_EQ (config.run (), 0);
|
||||
}
|
||||
|
||||
lym::Macro drc;
|
||||
drc.load_from (rs);
|
||||
EXPECT_EQ (drc.run (), 0);
|
||||
|
||||
db::Layout layout;
|
||||
|
||||
{
|
||||
tl::InputStream stream (output);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout);
|
||||
}
|
||||
|
||||
this->compare_layouts (layout, au, ut::NoNormalization);
|
||||
}
|
||||
|
||||
TEST(2)
|
||||
{
|
||||
std::string rs = ut::testsrc ();
|
||||
rs += "/testdata/drc/drcSimpleTests_2.drc";
|
||||
|
||||
std::string input = ut::testsrc ();
|
||||
input += "/testdata/drc/drctest.gds";
|
||||
|
||||
std::string au = ut::testsrc ();
|
||||
au += "/testdata/drc/drcSimpleTests_au2.gds";
|
||||
|
||||
std::string output = this->tmp_file ("tmp.gds");
|
||||
|
||||
{
|
||||
// Set some variables
|
||||
lym::Macro config;
|
||||
config.set_text (tl::sprintf (
|
||||
"$drc_test_source = \"%s\"\n"
|
||||
"$drc_test_target = \"%s\"\n"
|
||||
, input, output)
|
||||
);
|
||||
config.set_interpreter (lym::Macro::Ruby);
|
||||
EXPECT_EQ (config.run (), 0);
|
||||
}
|
||||
|
||||
lym::Macro drc;
|
||||
drc.load_from (rs);
|
||||
EXPECT_EQ (drc.run (), 0);
|
||||
|
||||
db::Layout layout;
|
||||
|
||||
{
|
||||
tl::InputStream stream (output);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout);
|
||||
}
|
||||
|
||||
this->compare_layouts (layout, au, ut::NoNormalization);
|
||||
}
|
||||
|
||||
TEST(3)
|
||||
{
|
||||
std::string rs = ut::testsrc ();
|
||||
rs += "/testdata/drc/drcSimpleTests_3.drc";
|
||||
|
||||
std::string input = ut::testsrc ();
|
||||
input += "/testdata/drc/drctest.gds";
|
||||
|
||||
std::string au = ut::testsrc ();
|
||||
au += "/testdata/drc/drcSimpleTests_au3.gds";
|
||||
|
||||
std::string output = this->tmp_file ("tmp.gds");
|
||||
|
||||
{
|
||||
// Set some variables
|
||||
lym::Macro config;
|
||||
config.set_text (tl::sprintf (
|
||||
"$drc_test_source = \"%s\"\n"
|
||||
"$drc_test_target = \"%s\"\n"
|
||||
, input, output)
|
||||
);
|
||||
config.set_interpreter (lym::Macro::Ruby);
|
||||
EXPECT_EQ (config.run (), 0);
|
||||
}
|
||||
|
||||
lym::Macro drc;
|
||||
drc.load_from (rs);
|
||||
EXPECT_EQ (drc.run (), 0);
|
||||
|
||||
db::Layout layout;
|
||||
|
||||
{
|
||||
tl::InputStream stream (output);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout);
|
||||
}
|
||||
|
||||
this->compare_layouts (layout, au, ut::NoNormalization);
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "utHead.h"
|
||||
#include "dbReader.h"
|
||||
#include "lymMacro.h"
|
||||
|
||||
void runtest (ut::TestBase *_this, int mode)
|
||||
{
|
||||
std::string rs = ut::testsrc ();
|
||||
rs += "/testdata/drc/drcSuiteTests.drc";
|
||||
|
||||
std::string input = ut::testsrc ();
|
||||
input += "/testdata/drc/drctest.gds";
|
||||
|
||||
std::string au = ut::testsrc ();
|
||||
au += "/testdata/drc/drcSuiteTests_au";
|
||||
au += tl::to_string (mode);
|
||||
au += ".gds";
|
||||
|
||||
std::string output = _this->tmp_file ("tmp.gds");
|
||||
|
||||
{
|
||||
// Set some variables
|
||||
lym::Macro config;
|
||||
config.set_text (tl::sprintf (
|
||||
"$drc_test_source = \"%s\"\n"
|
||||
"$drc_test_target = \"%s\"\n"
|
||||
"$drc_test_mode = %d\n"
|
||||
, input, output, mode)
|
||||
);
|
||||
config.set_interpreter (lym::Macro::Ruby);
|
||||
EXPECT_EQ (config.run (), 0);
|
||||
}
|
||||
|
||||
lym::Macro drc;
|
||||
drc.load_from (rs);
|
||||
EXPECT_EQ (drc.run (), 0);
|
||||
|
||||
db::Layout layout;
|
||||
|
||||
{
|
||||
tl::InputStream stream (output);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout);
|
||||
}
|
||||
|
||||
_this->compare_layouts (layout, au, ut::NoNormalization);
|
||||
}
|
||||
|
||||
TEST(1)
|
||||
{
|
||||
runtest (_this, 1);
|
||||
}
|
||||
|
||||
TEST(2)
|
||||
{
|
||||
test_is_long_runner ();
|
||||
runtest (_this, 2);
|
||||
}
|
||||
|
||||
TEST(3)
|
||||
{
|
||||
test_is_long_runner ();
|
||||
runtest (_this, 3);
|
||||
}
|
||||
|
||||
TEST(4)
|
||||
{
|
||||
test_is_long_runner ();
|
||||
runtest (_this, 4);
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ include($$PWD/../../lib_ut.pri)
|
|||
|
||||
SOURCES = \
|
||||
drcBasicTests.cc \
|
||||
drcSimpleTests.cc \
|
||||
drcSuiteTests.cc \
|
||||
|
||||
INCLUDEPATH += ../drc ../../rdb ../../db ../../tl ../../gsi ../../lym ../../ut
|
||||
DEPENDPATH += ../drc ../../rdb ../../db ../../tl ../../gsi ../../lym ../../ut
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ TEST(1_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -78,7 +78,7 @@ TEST(1_2)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -109,7 +109,7 @@ TEST(1_3)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -140,7 +140,7 @@ TEST(1_4)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -171,7 +171,7 @@ TEST(1_5)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -229,7 +229,7 @@ TEST(10_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -295,7 +295,7 @@ TEST(11_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -526,7 +526,7 @@ TEST(11_2)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -592,7 +592,7 @@ TEST(11_3)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -692,7 +692,7 @@ TEST(11_4)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -923,7 +923,7 @@ TEST(11_5)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1024,7 +1024,7 @@ TEST(11_6)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1067,7 +1067,7 @@ TEST(11_7)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1161,7 +1161,7 @@ TEST(12_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1211,7 +1211,7 @@ TEST(13_1)
|
|||
db::LayerMap map = reader.read (layout);
|
||||
EXPECT_EQ (map.to_string (), "layer_map('1/2 : \\'AA;L5A\\' (1/2)';'1/5 : \\'AA;L5A\\' (1/5)';'1/6 : \\'AA;L5A\\' (1/6)';'1/8 : \\'AA;L5A\\' (1/8)';'5/2 : \\'AA;L5A;H5A;E5A;I56A;E5L4\\' (5/2)';'5/5 : \\'AA;L5A;H5A;E5A;I56A;E5H4;E5I47\\' (5/5)';'5/6 : \\'AA;L5A;H5A;E5A;I56A;E5H4;E5I47\\' (5/6)';'5/8 : \\'AA;L5A;H5A;E5A;I56A;E5H4\\' (5/8)';'6/2 : \\'AA;H5A;I56A\\' (6/2)';'6/5 : \\'AA;H5A;I56A\\' (6/5)';'6/6 : \\'AA;H5A;I56A\\' (6/6)';'6/8 : \\'AA;H5A;I56A\\' (6/8)';'7/2 : \\'AA;H5A\\' (7/2)';'7/5 : \\'AA;H5A\\' (7/5)';'7/6 : \\'AA;H5A\\' (7/6)';'7/8 : \\'AA;H5A\\' (7/8)')")
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1261,7 +1261,7 @@ TEST(13_2)
|
|||
db::LayerMap map = reader.read (layout);
|
||||
EXPECT_EQ (map.to_string (), "layer_map('1/2 : \\'AA;L5A\\' (1/2)';'1/5 : \\'AA;L5A\\' (1/5)';'1/6 : \\'AA;L5A\\' (1/6)';'1/8 : \\'AA;L5A\\' (1/8)';'5/2 : \\'AA;L5A;H5A;E5A;I56A\\' (5/2)';'5/5 : \\'AA;L5A;H5A;E5A;I56A\\' (5/5)';'5/6 : \\'AA;L5A;H5A;E5A;I56A\\' (5/6)';'5/8 : \\'AA;L5A;H5A;E5A;I56A\\' (5/8)';'6/2 : \\'AA;H5A;I56A\\' (6/2)';'6/5 : \\'AA;H5A;I56A\\' (6/5)';'6/6 : \\'AA;H5A;I56A\\' (6/6)';'6/8 : \\'AA;H5A;I56A\\' (6/8)';'7/2 : \\'AA;H5A\\' (7/2)';'7/5 : \\'AA;H5A\\' (7/5)';'7/6 : \\'AA;H5A\\' (7/6)';'7/8 : \\'AA;H5A\\' (7/8)')");
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1327,7 +1327,7 @@ TEST(13_3)
|
|||
db::LayerMap map = reader.read (layout);
|
||||
EXPECT_EQ (map.to_string (), "layer_map('1/2 : \\'TAA;TL5A;AA;L5A\\' (1/2)';'1/5 : \\'TAA;TL5A;AA;L5A\\' (1/5)';'1/6 : \\'TAA;TL5A;AA;L5A\\' (1/6)';'1/8 : \\'TAA;TL5A;AA;L5A\\' (1/8)';'5/2 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/2)';'5/5 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/5)';'5/6 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/6)';'5/8 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/8)';'6/2 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/2)';'6/5 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/5)';'6/6 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/6)';'6/8 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/8)';'7/2 : \\'TAA;TH5A;AA;H5A\\' (7/2)';'7/5 : \\'TAA;TH5A;AA;H5A\\' (7/5)';'7/6 : \\'TAA;TH5A;AA;H5A\\' (7/6)';'7/8 : \\'TAA;TH5A;AA;H5A\\' (7/8)')");
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1393,7 +1393,7 @@ TEST(13_4)
|
|||
db::LayerMap map = reader.read (layout);
|
||||
EXPECT_EQ (map.to_string (), "layer_map('1/2 : \\'TAA;TL5A;AA;L5A\\' (1/2)';'1/5 : \\'TAA;TL5A;AA;L5A\\' (1/5)';'1/6 : \\'TAA;TL5A;AA;L5A\\' (1/6)';'1/8 : \\'TAA;TL5A;AA;L5A\\' (1/8)';'5/2 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/2)';'5/5 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/5)';'5/6 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/6)';'5/8 : \\'TAA;TL5A;TH5A;TE5A;TI56A;AA;L5A;H5A;E5A;I56A\\' (5/8)';'6/2 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/2)';'6/5 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/5)';'6/6 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/6)';'6/8 : \\'TAA;TH5A;TI56A;AA;H5A;I56A\\' (6/8)';'7/2 : \\'TAA;TH5A;AA;H5A\\' (7/2)';'7/5 : \\'TAA;TH5A;AA;H5A\\' (7/5)';'7/6 : \\'TAA;TH5A;AA;H5A\\' (7/6)';'7/8 : \\'TAA;TH5A;AA;H5A\\' (7/8)')");
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1436,7 +1436,7 @@ TEST(14_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1469,7 +1469,7 @@ TEST(2_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1504,7 +1504,7 @@ TEST(2_2)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1561,7 +1561,7 @@ TEST(2_4)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1618,7 +1618,7 @@ TEST(2_6)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1869,7 +1869,7 @@ TEST(3_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -1903,7 +1903,7 @@ TEST(3_10)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -2176,7 +2176,7 @@ TEST(3_12)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -2427,7 +2427,7 @@ TEST(3_2)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -2722,7 +2722,7 @@ TEST(3_5)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -2822,7 +2822,7 @@ TEST(3_9)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -2879,7 +2879,7 @@ TEST(4_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3008,7 +3008,7 @@ TEST(4_2)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3077,7 +3077,7 @@ TEST(5_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3113,7 +3113,7 @@ TEST(5_2)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3291,7 +3291,7 @@ TEST(5_3)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3354,7 +3354,7 @@ TEST(6_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3488,7 +3488,7 @@ TEST(7_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3540,7 +3540,7 @@ TEST(8_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3583,7 +3583,7 @@ TEST(8_2)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3626,7 +3626,7 @@ TEST(8_3)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3669,7 +3669,7 @@ TEST(8_4)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3712,7 +3712,7 @@ TEST(8_5)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3759,7 +3759,7 @@ TEST(8_6)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3802,7 +3802,7 @@ TEST(8_7)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3845,7 +3845,7 @@ TEST(8_8)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3945,7 +3945,7 @@ TEST(9_1)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -3984,7 +3984,7 @@ TEST(9_2)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -4036,7 +4036,7 @@ TEST(99)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
@ -4053,7 +4053,7 @@ TEST(99)
|
|||
try {
|
||||
db::LayerMap map = reader.read (layout);
|
||||
} catch (tl::Exception &ex) {
|
||||
ut::print_error (ex.msg ());
|
||||
tl::error << ex.msg ();
|
||||
error = true;
|
||||
}
|
||||
EXPECT_EQ (error, false)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "tlEvents.h"
|
||||
|
||||
#include "utHead.h"
|
||||
#include <memory>
|
||||
|
||||
// Object with event
|
||||
class Observed
|
||||
|
|
|
|||
|
|
@ -11,10 +11,14 @@ TEMPLATE = lib
|
|||
# Input
|
||||
HEADERS = \
|
||||
utHead.h \
|
||||
utCommon.h
|
||||
utTestBase.h \
|
||||
utTestConsole.h \
|
||||
utCommon.h
|
||||
|
||||
SOURCES = \
|
||||
utMain.cc \
|
||||
utTestConsole.cc \
|
||||
utTestBase.cc \
|
||||
|
||||
INCLUDEPATH = ../tl ../db ../gsi ../lay ../ext ../lib
|
||||
DEPENDPATH = ../tl ../db ../gsi ../lay ../ext ../lib
|
||||
|
|
|
|||
472
src/ut/utHead.h
472
src/ut/utHead.h
|
|
@ -24,482 +24,20 @@
|
|||
#ifndef HDR_utHead
|
||||
#define HDR_utHead
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
|
||||
#include "utCommon.h"
|
||||
#include "tlString.h"
|
||||
#include "tlException.h"
|
||||
#include "tlStaticObjects.h"
|
||||
#include "dbStatic.h"
|
||||
#include "dbTypes.h"
|
||||
#include "gsiExpression.h"
|
||||
#include "gsiInterpreter.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace db {
|
||||
class Layout;
|
||||
class LayerMap;
|
||||
}
|
||||
|
||||
namespace rba {
|
||||
class RubyInterpreter;
|
||||
}
|
||||
|
||||
namespace pya {
|
||||
class PythonInterpreter;
|
||||
}
|
||||
#include "utTestBase.h"
|
||||
#include "utTestConsole.h"
|
||||
|
||||
namespace ut {
|
||||
|
||||
extern tl::LogTee ctrl;
|
||||
extern tl::LogTee noctrl;
|
||||
|
||||
/**
|
||||
* @brief The unit test execution function
|
||||
*/
|
||||
UT_PUBLIC int main (int argc, char **argv);
|
||||
|
||||
/**
|
||||
* @brief Prints a error message to the unit test output stream
|
||||
*/
|
||||
UT_PUBLIC void print_error (const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief A detailed diff printer
|
||||
*/
|
||||
UT_PUBLIC void write_detailed_diff (std::ostream &os, const std::string &subject, const std::string &ref);
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the test is run in verbose mode
|
||||
* Verbose mode is enabled through the "-v" option
|
||||
*/
|
||||
UT_PUBLIC bool verbose ();
|
||||
|
||||
/**
|
||||
* @brief Returns the Ruby interpreter
|
||||
*/
|
||||
UT_PUBLIC rba::RubyInterpreter *ruby_interpreter ();
|
||||
|
||||
/**
|
||||
* @brief Returns the Python interpreter
|
||||
*/
|
||||
UT_PUBLIC pya::PythonInterpreter *python_interpreter ();
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the unit test is run in debug mode
|
||||
* In debug mode, the unit tests shall offer information on how to fix the
|
||||
* test. Specifically if layout compare is involved, it shall display the golden
|
||||
* file name and the actual one and terminate to allow updating the files.
|
||||
*/
|
||||
UT_PUBLIC bool is_debug_mode ();
|
||||
|
||||
/**
|
||||
* @brief Specifies the normalization mode for compare_layouts
|
||||
*/
|
||||
enum NormalizationMode
|
||||
{
|
||||
NoNormalization, // no normalization - take the test subject as it is
|
||||
WriteGDS2, // normalize subject by writing to GDS2 and reading back
|
||||
WriteOAS // normalize subject by writing to OASIS and reading back
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Gets the path of the test data
|
||||
* This path is specified through the environment variable $TESTSRC
|
||||
*/
|
||||
UT_PUBLIC std::string testsrc ();
|
||||
|
||||
/**
|
||||
* @brief Gets the path of the private test data
|
||||
* This path is specified through the environment variable $TESTSRC and the
|
||||
* private testdata directory. If no private test data is available, this
|
||||
* method will throw a CancelException which makes the test skipped.
|
||||
*/
|
||||
UT_PUBLIC std::string testsrc_private ();
|
||||
|
||||
/**
|
||||
* @brief A basic exception for the unit test framework
|
||||
*/
|
||||
struct Exception
|
||||
: public tl::Exception
|
||||
{
|
||||
Exception (const std::string &msg)
|
||||
: tl::Exception (msg)
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A utility class to capture the warning, error and info channels
|
||||
*
|
||||
* Instantiate this class inside a test. Then run the test and finally
|
||||
* obtain the collected output with CaptureChannel::captured_text().
|
||||
*/
|
||||
class UT_PUBLIC CaptureChannel : public tl::Channel
|
||||
{
|
||||
public:
|
||||
CaptureChannel ();
|
||||
|
||||
std::string captured_text () const
|
||||
{
|
||||
return m_text.str ();
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
m_text.str (std::string ());
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void puts (const char *s);
|
||||
virtual void endl ();
|
||||
virtual void end ();
|
||||
virtual void begin ();
|
||||
|
||||
private:
|
||||
std::ostringstream m_text;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A generic compare operator
|
||||
*/
|
||||
template <class X, class Y>
|
||||
inline bool equals (const X &a, const Y &b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of the compare operator for doubles
|
||||
*/
|
||||
UT_PUBLIC bool equals (double a, double b);
|
||||
|
||||
/**
|
||||
* @brief Specialization of comparison of pointers vs. integers (specifically "0")
|
||||
*/
|
||||
template <class X>
|
||||
inline bool equals (X *a, int b)
|
||||
{
|
||||
return a == (X *) size_t (b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of comparison of double vs "anything"
|
||||
*/
|
||||
template <class Y>
|
||||
inline bool equals (double a, const Y &b)
|
||||
{
|
||||
return equals (a, double (b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of comparison of "anything" vs. double
|
||||
*/
|
||||
template <class X>
|
||||
inline bool equals (const X &a, double b)
|
||||
{
|
||||
return equals (double (a), b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of the compare operator for const char *
|
||||
*/
|
||||
inline bool equals (const char *a, const char *b)
|
||||
{
|
||||
return equals (std::string (a), std::string (b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of the compare operator for std::string vs. const char *
|
||||
*/
|
||||
inline bool equals (const std::string &a, const char *b)
|
||||
{
|
||||
return equals (a, std::string (b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of the compare operator for std::string vs. const char *
|
||||
*/
|
||||
inline bool equals (const char *a, const std::string &b)
|
||||
{
|
||||
return equals (std::string (a), b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The base class for tests
|
||||
*/
|
||||
struct UT_PUBLIC TestBase
|
||||
{
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
TestBase (const std::string &file, const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~TestBase () { }
|
||||
|
||||
/**
|
||||
* @brief Actually runs the test
|
||||
* @return True, if the test was successful
|
||||
*/
|
||||
bool do_test (const std::string &mode);
|
||||
|
||||
/**
|
||||
* @brief Raises an exception with the given string
|
||||
* This version prints the last checkpoint for reference.
|
||||
*/
|
||||
void raise (const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Raises an exception with the given string, file and line number
|
||||
*/
|
||||
void raise (const std::string &file, int line, const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Registers a checkpoint
|
||||
*/
|
||||
void checkpoint (const std::string &file, int line);
|
||||
|
||||
/**
|
||||
* @brief Resets the checkpoints set
|
||||
*/
|
||||
void reset_checkpoint ();
|
||||
|
||||
/**
|
||||
* @brief Compares a layout with a golden layout file
|
||||
* @param layout The layout to compare
|
||||
* @param au_file The golden file path
|
||||
* @param norm The normalization mode (see NormalizationMode)
|
||||
* @param tolerance A tolerance applied when comparing shapes in database units
|
||||
* The layout is normalized by writing to a file and reading back
|
||||
*/
|
||||
void compare_layouts (const db::Layout &layout, const std::string &au_file, NormalizationMode norm = WriteGDS2, db::Coord tolerance = 0);
|
||||
|
||||
/**
|
||||
* @brief Compares a layout with a golden layout file with layer mapping
|
||||
* @param layout The layout to compare
|
||||
* @param au_file The golden file path
|
||||
* @param lmap The layer mapping object
|
||||
* @param read_all_others If true, all other layers will be read too
|
||||
* @param norm The normalization mode (see NormalizationMode)
|
||||
* @param tolerance A tolerance applied when comparing shapes in database units
|
||||
* The layout is normalized by writing to a file and reading back
|
||||
*/
|
||||
void compare_layouts (const db::Layout &layout, const std::string &au_file, const db::LayerMap &lmap, bool read_all_others, NormalizationMode norm = WriteGDS2, db::Coord tolerance = 0);
|
||||
|
||||
/**
|
||||
* @brief Compares two text files
|
||||
*/
|
||||
void compare_text_files (const std::string &path_a, const std::string &path_b);
|
||||
|
||||
/**
|
||||
* @brief The test's name
|
||||
* @return The name of the test
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_test;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares a temporary file path
|
||||
* @param fn The actual name of the file
|
||||
* @return A path suitable for writing a temporary file
|
||||
* The directory for the file will be created within this method.
|
||||
*/
|
||||
std::string tmp_file (const std::string &fn = "tmp") const;
|
||||
|
||||
/**
|
||||
* @brief Removes all temporary files
|
||||
*/
|
||||
void remove_tmp_folder ();
|
||||
|
||||
/**
|
||||
* @brief A generic diff printer
|
||||
*/
|
||||
template <class X, class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, const X &subject, const Y & /*ref*/)
|
||||
{
|
||||
std::ostringstream sstr;
|
||||
sstr << msg << " (actual value is " << subject << ")";
|
||||
raise (file, line, sstr.str ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A generic diff printer
|
||||
*/
|
||||
template <class X, class Y>
|
||||
void detailed_diff (const std::string &file, int line, const std::string &msg, const X &subject, const Y &ref)
|
||||
{
|
||||
std::ostringstream sstr;
|
||||
sstr << msg << std::endl;
|
||||
ut::write_detailed_diff (sstr, tl::to_string (subject), tl::to_string (ref));
|
||||
raise (file, line, sstr.str ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for int vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, int subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for unsigned int vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, unsigned int subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for long vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, long subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for unsigned long vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, unsigned long subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for long long vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, long long subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for unsigned long long vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, unsigned long long subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for bool
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, bool subject, bool ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for double
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, double subject, double ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for strings
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, const std::string &subject, const std::string &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for strings vs. const char *
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, const std::string &subject, const char *ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for strings vs. const char *
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, const char *subject, const std::string &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for C strings
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, const char *subject, const char *ref)
|
||||
{
|
||||
diff (file, line, msg, std::string (subject), std::string (ref));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main entry point for the compare feature (EXPECT_EQ and EXPECT_NE)
|
||||
*/
|
||||
template <class T1, class T2>
|
||||
void eq_helper (bool eq, const T1 &a, const T2 &b, const char *what_expr, const char *equals_expr, const char *file, int line)
|
||||
{
|
||||
if (ut::equals (a, b) != eq) {
|
||||
std::ostringstream sstr;
|
||||
sstr << what_expr << " does not equal " << equals_expr;
|
||||
diff (file, line, sstr.str (), a, b);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void execute (ut::TestBase *_this) throw (tl::Exception) = 0;
|
||||
|
||||
std::string m_test;
|
||||
std::string m_testdir;
|
||||
// last checkpoint
|
||||
std::string m_cp_file;
|
||||
int m_cp_line;
|
||||
bool m_any_failed;
|
||||
QString m_testtmp;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The registration facility for tests
|
||||
*/
|
||||
struct Registrar
|
||||
{
|
||||
static void reg (ut::TestBase *t)
|
||||
{
|
||||
if (! ms_instance) {
|
||||
ms_instance = new Registrar ();
|
||||
}
|
||||
ms_instance->m_tests.push_back (t);
|
||||
}
|
||||
|
||||
static Registrar *instance ()
|
||||
{
|
||||
return ms_instance;
|
||||
}
|
||||
|
||||
const std::vector <ut::TestBase *> &tests () const
|
||||
{
|
||||
return m_tests;
|
||||
}
|
||||
|
||||
private:
|
||||
static Registrar *ms_instance;
|
||||
|
||||
Registrar () : m_tests () { }
|
||||
|
||||
std::vector <ut::TestBase *> m_tests;
|
||||
};
|
||||
|
||||
} // namespace ut
|
||||
|
||||
#define TEST(NAME) \
|
||||
|
|
|
|||
1545
src/ut/utMain.cc
1545
src/ut/utMain.cc
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,458 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "utTestBase.h"
|
||||
#include "utTestConsole.h"
|
||||
#include "utHead.h"
|
||||
#include "tlFileUtils.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlStream.h"
|
||||
#include "dbLayout.h"
|
||||
#include "dbStreamLayers.h"
|
||||
#include "dbGDS2Writer.h"
|
||||
#include "dbOASISWriter.h"
|
||||
#include "dbReader.h"
|
||||
#include "dbCommonReader.h"
|
||||
#include "dbLayoutDiff.h"
|
||||
|
||||
#include "pya.h"
|
||||
#include "rba.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
namespace ut
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
static bool s_verbose_flag = false;
|
||||
static bool s_debug_mode = false;
|
||||
static bool s_continue_flag = false;
|
||||
|
||||
bool verbose ()
|
||||
{
|
||||
return s_verbose_flag;
|
||||
}
|
||||
|
||||
void set_verbose (bool f)
|
||||
{
|
||||
s_verbose_flag = f;
|
||||
}
|
||||
|
||||
void set_continue_flag (bool f)
|
||||
{
|
||||
s_continue_flag = f;
|
||||
}
|
||||
|
||||
bool is_debug_mode ()
|
||||
{
|
||||
return s_debug_mode;
|
||||
}
|
||||
|
||||
void set_debug_mode (bool f)
|
||||
{
|
||||
s_debug_mode = f;
|
||||
}
|
||||
|
||||
pya::PythonInterpreter *python_interpreter ()
|
||||
{
|
||||
pya::PythonInterpreter *ip = pya::PythonInterpreter::instance ();
|
||||
tl_assert (ip != 0);
|
||||
return ip;
|
||||
}
|
||||
|
||||
rba::RubyInterpreter *ruby_interpreter ()
|
||||
{
|
||||
rba::RubyInterpreter *ip = rba::RubyInterpreter::instance ();
|
||||
tl_assert (ip != 0);
|
||||
return ip;
|
||||
}
|
||||
|
||||
std::string testsrc ()
|
||||
{
|
||||
const char *ts = getenv ("TESTSRC");
|
||||
if (! ts) {
|
||||
throw tl::Exception ("TESTSRC undefined");
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
|
||||
std::string testsrc_private ()
|
||||
{
|
||||
QDir d (QDir (tl::to_qstring (ut::testsrc ())).filePath (QString::fromUtf8 ("private")));
|
||||
if (! d.exists ()) {
|
||||
throw tl::CancelException ();
|
||||
}
|
||||
return tl::to_string (d.path ());
|
||||
}
|
||||
|
||||
std::string testtmp ()
|
||||
{
|
||||
// Ensures the test temp directory is present
|
||||
const char *tt = getenv ("TESTTMP");
|
||||
if (! tt) {
|
||||
throw tl::Exception ("TESTTMP undefined");
|
||||
}
|
||||
return tt;
|
||||
}
|
||||
|
||||
bool equals (double a, double b)
|
||||
{
|
||||
double m = fabs (0.5 * (a + b));
|
||||
if (m < 1e-30) {
|
||||
// resolution limit is 1e-30
|
||||
return true;
|
||||
} else {
|
||||
double d = fabs (a - b);
|
||||
// we consider two values equal for the purpose of unit tests if they have the
|
||||
// same value within 1e-10 (0.00000001%).
|
||||
return d < 1e-10 * m;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move this to tlString.h
|
||||
static std::string replicate (const char *s, size_t n)
|
||||
{
|
||||
std::string res;
|
||||
res.reserve (strlen (s) * n);
|
||||
while (n > 0) {
|
||||
res += s;
|
||||
--n;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// TestBase implementation
|
||||
|
||||
ut::Registrar *ut::Registrar::ms_instance = 0;
|
||||
|
||||
TestBase::TestBase (const std::string &file, const std::string &name)
|
||||
: m_editable (false), m_slow (false), m_cp_line (0), m_any_failed (false)
|
||||
{
|
||||
QFileInfo f (tl::to_qstring (file));
|
||||
m_test = tl::to_string (f.baseName ()) + ":" + name;
|
||||
m_testdir = tl::to_string (f.baseName ()) + "_" + name;
|
||||
ut::Registrar::reg (this);
|
||||
}
|
||||
|
||||
bool TestBase::do_test (bool editable, bool slow)
|
||||
{
|
||||
m_editable = editable;
|
||||
m_slow = slow;
|
||||
|
||||
ut::ctrl << "<system-out>";
|
||||
|
||||
try {
|
||||
|
||||
// Ensures the test temp directory is present
|
||||
QDir dir (tl::to_qstring (testtmp ()));
|
||||
QDir tmpdir (dir.absoluteFilePath (tl::to_qstring (m_testdir)));
|
||||
if (tmpdir.exists () && ! tl::rm_dir_recursive (tmpdir.absolutePath ())) {
|
||||
throw tl::Exception ("Unable to clean temporary dir: " + tl::to_string (tmpdir.absolutePath ()));
|
||||
}
|
||||
if (! dir.mkpath (tl::to_qstring (m_testdir))) {
|
||||
throw tl::Exception ("Unable to create path for temporary files: " + tl::to_string (tmpdir.absolutePath ()));
|
||||
}
|
||||
dir.cd (tl::to_qstring (m_testdir));
|
||||
|
||||
m_testtmp = dir.absolutePath ();
|
||||
|
||||
static std::string testname_value;
|
||||
static std::string testtmp_value;
|
||||
|
||||
putenv (const_cast<char *> ("TESTNAME="));
|
||||
testname_value = std::string ("TESTNAME=") + m_test;
|
||||
putenv (const_cast<char *> (testname_value.c_str ()));
|
||||
|
||||
putenv (const_cast<char *> ("TESTTMP_WITH_NAME="));
|
||||
testtmp_value = std::string ("TESTTMP_WITH_NAME=") + m_testtmp.toUtf8().constData();
|
||||
putenv (const_cast<char *> (testtmp_value.c_str ()));
|
||||
|
||||
reset_checkpoint ();
|
||||
|
||||
tl::Timer timer;
|
||||
timer.start();
|
||||
|
||||
execute (this);
|
||||
|
||||
timer.stop();
|
||||
|
||||
m_testtmp.clear ();
|
||||
|
||||
ut::ctrl << "</system-out>";
|
||||
|
||||
ut::noctrl << "Time: " << timer.sec_wall () << "s (wall) " << timer.sec_user () << "s (user) " << timer.sec_sys () << "s (sys)";
|
||||
ut::ctrl << "<x-testcase-times wall=\"" << timer.sec_wall () << "\" user=\"" << timer.sec_user () << "\" sys=\"" << timer.sec_sys () << "\"/>";
|
||||
|
||||
} catch (...) {
|
||||
ut::ctrl << "</system-out>";
|
||||
throw;
|
||||
}
|
||||
|
||||
return (!m_any_failed);
|
||||
}
|
||||
|
||||
std::string TestBase::tmp_file (const std::string &fn) const
|
||||
{
|
||||
tl_assert (! m_testtmp.isEmpty ());
|
||||
QDir dir (m_testtmp);
|
||||
return tl::to_string (dir.absoluteFilePath (tl::to_qstring (fn)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recursively empties a directory
|
||||
*/
|
||||
static void empty_dir (QDir dir)
|
||||
{
|
||||
QStringList entries = dir.entryList (QDir::AllEntries | QDir::NoDotAndDotDot);
|
||||
for (QStringList::const_iterator e = entries.begin (); e != entries.end (); ++e) {
|
||||
QString epath = dir.absoluteFilePath (*e);
|
||||
if (QFileInfo (epath).isDir ()) {
|
||||
empty_dir (QDir (epath));
|
||||
dir.rmdir (*e);
|
||||
} else if (! dir.remove (*e)) {
|
||||
throw tl::Exception ("Unable to remove file or directory: " + tl::to_string (dir.filePath (*e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestBase::remove_tmp_folder ()
|
||||
{
|
||||
// Ensures the test temp directory is present
|
||||
QDir dir (tl::to_qstring (testtmp ()));
|
||||
if (dir.cd (tl::to_qstring (m_test))) {
|
||||
|
||||
empty_dir (dir);
|
||||
|
||||
dir.cdUp ();
|
||||
if (! dir.rmdir (tl::to_qstring (m_test))) {
|
||||
throw tl::Exception ("Unable to remove directory: " + tl::to_string (dir.filePath (tl::to_qstring (m_test))));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void TestBase::checkpoint (const std::string &file, int line)
|
||||
{
|
||||
m_cp_file = file;
|
||||
m_cp_line = line;
|
||||
}
|
||||
|
||||
void TestBase::reset_checkpoint ()
|
||||
{
|
||||
m_cp_file = std::string ();
|
||||
m_cp_line = 0;
|
||||
}
|
||||
|
||||
void TestBase::raise (const std::string &file, int line, const std::string &msg)
|
||||
{
|
||||
std::ostringstream sstr;
|
||||
sstr << file << ", line " << line << ": " << msg;
|
||||
if (s_continue_flag) {
|
||||
tl::error << sstr.str ();
|
||||
m_any_failed = true;
|
||||
} else {
|
||||
throw ut::Exception (sstr.str ());
|
||||
}
|
||||
}
|
||||
|
||||
void TestBase::raise (const std::string &msg)
|
||||
{
|
||||
std::ostringstream sstr;
|
||||
if (m_cp_line > 0) {
|
||||
sstr << "(last checkpoint: " << m_cp_file << ", line " << m_cp_line << "): ";
|
||||
}
|
||||
sstr << msg;
|
||||
if (s_continue_flag) {
|
||||
tl::error << sstr.str ();
|
||||
m_any_failed = true;
|
||||
} else {
|
||||
throw ut::Exception (sstr.str ());
|
||||
}
|
||||
}
|
||||
|
||||
void TestBase::test_is_editable_only ()
|
||||
{
|
||||
if (!m_editable) {
|
||||
throw tl::CancelException ();
|
||||
}
|
||||
}
|
||||
|
||||
void TestBase::test_is_non_editable_only ()
|
||||
{
|
||||
if (m_editable) {
|
||||
throw tl::CancelException ();
|
||||
}
|
||||
}
|
||||
|
||||
void TestBase::test_is_long_runner ()
|
||||
{
|
||||
if (!m_slow) {
|
||||
throw tl::CancelException ();
|
||||
}
|
||||
}
|
||||
|
||||
void TestBase::compare_layouts (const db::Layout &layout, const std::string &au_file, NormalizationMode norm, db::Coord tolerance)
|
||||
{
|
||||
compare_layouts (layout, au_file, db::LayerMap (), true, norm, tolerance);
|
||||
}
|
||||
|
||||
void TestBase::compare_layouts (const db::Layout &layout, const std::string &au_file, const db::LayerMap &lm, bool read_other_layers, NormalizationMode norm, db::Coord tolerance)
|
||||
{
|
||||
// normalize the layout by writing to GDS and reading from ..
|
||||
|
||||
// generate a "unique" name ...
|
||||
unsigned int hash = 0;
|
||||
for (const char *cp = au_file.c_str (); *cp; ++cp) {
|
||||
hash = (hash << 4) ^ (hash >> 4) ^ ((unsigned int) *cp);
|
||||
}
|
||||
|
||||
std::string tmp_file;
|
||||
|
||||
if (norm == WriteGDS2) {
|
||||
|
||||
tmp_file = ut::TestBase::tmp_file (tl::sprintf ("tmp_%x.gds", hash));
|
||||
|
||||
tl::OutputStream stream (tmp_file.c_str ());
|
||||
db::GDS2Writer writer;
|
||||
db::SaveLayoutOptions options;
|
||||
writer.write (const_cast<db::Layout &> (layout), stream, options);
|
||||
|
||||
} else {
|
||||
|
||||
tmp_file = ut::TestBase::tmp_file (tl::sprintf ("tmp_%x.oas", hash));
|
||||
|
||||
tl::OutputStream stream (tmp_file.c_str ());
|
||||
db::OASISWriter writer;
|
||||
db::SaveLayoutOptions options;
|
||||
writer.write (const_cast<db::Layout &> (layout), stream, options);
|
||||
|
||||
}
|
||||
|
||||
const db::Layout *subject = 0;
|
||||
db::Layout layout2;
|
||||
|
||||
if (norm != NoNormalization) {
|
||||
|
||||
// read all layers from the original layout, so the layer table is the same
|
||||
for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) {
|
||||
layout2.insert_layer ((*l).first, *(*l).second);
|
||||
}
|
||||
|
||||
tl::InputStream stream (tmp_file);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout2);
|
||||
|
||||
subject = &layout2;
|
||||
|
||||
} else {
|
||||
subject = &layout;
|
||||
}
|
||||
|
||||
bool equal = false;
|
||||
bool any = false;
|
||||
|
||||
int n = 0;
|
||||
for ( ; ! equal; ++n) {
|
||||
|
||||
db::Layout layout_au;
|
||||
|
||||
// read all layers from the original layout, so the layer table is the same
|
||||
for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) {
|
||||
layout_au.insert_layer ((*l).first, *(*l).second);
|
||||
}
|
||||
|
||||
db::LoadLayoutOptions options;
|
||||
options.get_options<db::CommonReaderOptions> ().layer_map = lm;
|
||||
options.get_options<db::CommonReaderOptions> ().create_other_layers = read_other_layers;
|
||||
|
||||
std::string fn = au_file;
|
||||
if (n > 0) {
|
||||
fn += tl::sprintf (".%d", n);
|
||||
}
|
||||
|
||||
if (QFileInfo (tl::to_qstring (fn)).exists ()) {
|
||||
|
||||
if (n == 1 && any) {
|
||||
throw tl::Exception (tl::sprintf ("Inconsistent reference variants for %s: there can be either variants (.1,.2,... suffix) or a single file (without suffix)", au_file));
|
||||
}
|
||||
|
||||
any = true;
|
||||
|
||||
tl::InputStream stream (fn);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout_au, options);
|
||||
|
||||
equal = db::compare_layouts (*subject, layout_au, (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) | db::layout_diff::f_flatten_array_insts /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/, tolerance, 100 /*max diff lines*/);
|
||||
if (equal && n > 0) {
|
||||
tl::info << tl::sprintf ("Found match on golden reference variant %s", fn);
|
||||
}
|
||||
|
||||
} else if (n > 0) {
|
||||
if (! any) {
|
||||
tl::warn << tl::sprintf ("No golden data found (%s)", au_file);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! equal) {
|
||||
raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s%s",
|
||||
tl::to_string (QFileInfo (tl::to_qstring (tmp_file)).absoluteFilePath ()),
|
||||
tl::to_string (QFileInfo (tl::to_qstring (au_file)).absoluteFilePath ()),
|
||||
(n > 1 ? "\nand variants" : "")));
|
||||
}
|
||||
}
|
||||
|
||||
void TestBase::write_detailed_diff (std::ostream &os, const std::string &subject, const std::string &ref)
|
||||
{
|
||||
os << replicate (" ", TestConsole::instance ()->indent ()) << "Actual value is: " << tl::to_string (subject) << std::endl
|
||||
<< replicate (" ", TestConsole::instance ()->indent ()) << "Reference value is: " << tl::to_string (ref) << std::endl
|
||||
;
|
||||
}
|
||||
|
||||
static std::string read_file (const std::string &path)
|
||||
{
|
||||
QFile file (tl::to_qstring (path));
|
||||
if (! file.open (QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
tl::warn << tl::sprintf ("Unable to open file %s", path);
|
||||
}
|
||||
|
||||
QByteArray ba = file.readAll ();
|
||||
return std::string (ba.constData (), 0, ba.size ());
|
||||
}
|
||||
|
||||
void TestBase::compare_text_files (const std::string &path_a, const std::string &path_b)
|
||||
{
|
||||
std::string text_a = read_file (path_a);
|
||||
std::string text_b = read_file (path_b);
|
||||
|
||||
if (text_a != text_b) {
|
||||
raise (tl::sprintf ("Compare failed - see:\n file 1: %s\n file 2: %s",
|
||||
tl::to_string (QFileInfo (tl::to_qstring (path_a)).absoluteFilePath ()),
|
||||
tl::to_string (QFileInfo (tl::to_qstring (path_b)).absoluteFilePath ())));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,498 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_utTestBase
|
||||
#define HDR_utTestBase
|
||||
|
||||
#include "utCommon.h"
|
||||
#include "dbTypes.h"
|
||||
#include "tlException.h"
|
||||
#include "tlString.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <QString>
|
||||
|
||||
namespace db
|
||||
{
|
||||
class Layout;
|
||||
class LayerMap;
|
||||
}
|
||||
|
||||
namespace rba
|
||||
{
|
||||
class RubyInterpreter;
|
||||
}
|
||||
|
||||
namespace pya
|
||||
{
|
||||
class PythonInterpreter;
|
||||
}
|
||||
|
||||
namespace ut
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the test is run in verbose mode
|
||||
* Verbose mode is enabled through the "-v" option
|
||||
*/
|
||||
UT_PUBLIC bool verbose ();
|
||||
|
||||
/**
|
||||
* @brief Sets verbose mode
|
||||
*/
|
||||
UT_PUBLIC void set_verbose (bool v);
|
||||
|
||||
/**
|
||||
* @brief Enables or disables "continue" mode
|
||||
* In continue mode, the execution will proceed even in case of an error.
|
||||
*/
|
||||
UT_PUBLIC void set_continue_flag (bool f);
|
||||
|
||||
/**
|
||||
* @brief Returns the Ruby interpreter
|
||||
*/
|
||||
UT_PUBLIC rba::RubyInterpreter *ruby_interpreter ();
|
||||
|
||||
/**
|
||||
* @brief Returns the Python interpreter
|
||||
*/
|
||||
UT_PUBLIC pya::PythonInterpreter *python_interpreter ();
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the unit test is run in debug mode
|
||||
* In debug mode, the unit tests shall offer information on how to fix the
|
||||
* test. Specifically if layout compare is involved, it shall display the golden
|
||||
* file name and the actual one and terminate to allow updating the files.
|
||||
*/
|
||||
UT_PUBLIC bool is_debug_mode ();
|
||||
|
||||
/**
|
||||
* @brief Enables or disables debug mode
|
||||
*/
|
||||
UT_PUBLIC void set_debug_mode (bool f);
|
||||
|
||||
/**
|
||||
* @brief Gets the path of the test data
|
||||
* This path is specified through the environment variable $TESTSRC
|
||||
*/
|
||||
UT_PUBLIC std::string testsrc ();
|
||||
|
||||
/**
|
||||
* @brief Gets the path of the private test data
|
||||
* This path is specified through the environment variable $TESTSRC and the
|
||||
* private testdata directory. If no private test data is available, this
|
||||
* method will throw a CancelException which makes the test skipped.
|
||||
*/
|
||||
UT_PUBLIC std::string testsrc_private ();
|
||||
|
||||
/**
|
||||
* @brief Gets the path to the temporary data
|
||||
* This path is specified through the environment variable $TESTTMP
|
||||
*/
|
||||
UT_PUBLIC std::string testtmp ();
|
||||
|
||||
/**
|
||||
* @brief A basic exception for the unit test framework
|
||||
*/
|
||||
struct Exception
|
||||
: public tl::Exception
|
||||
{
|
||||
Exception (const std::string &msg)
|
||||
: tl::Exception (msg)
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Specifies the normalization mode for compare_layouts
|
||||
*/
|
||||
enum NormalizationMode
|
||||
{
|
||||
NoNormalization, // no normalization - take the test subject as it is
|
||||
WriteGDS2, // normalize subject by writing to GDS2 and reading back
|
||||
WriteOAS // normalize subject by writing to OASIS and reading back
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A generic compare operator
|
||||
*/
|
||||
template <class X, class Y>
|
||||
inline bool equals (const X &a, const Y &b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of the compare operator for doubles
|
||||
*/
|
||||
UT_PUBLIC bool equals (double a, double b);
|
||||
|
||||
/**
|
||||
* @brief Specialization of comparison of pointers vs. integers (specifically "0")
|
||||
*/
|
||||
template <class X>
|
||||
inline bool equals (X *a, int b)
|
||||
{
|
||||
return a == (X *) size_t (b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of comparison of double vs "anything"
|
||||
*/
|
||||
template <class Y>
|
||||
inline bool equals (double a, const Y &b)
|
||||
{
|
||||
return equals (a, double (b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of comparison of "anything" vs. double
|
||||
*/
|
||||
template <class X>
|
||||
inline bool equals (const X &a, double b)
|
||||
{
|
||||
return equals (double (a), b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of the compare operator for const char *
|
||||
*/
|
||||
inline bool equals (const char *a, const char *b)
|
||||
{
|
||||
return equals (std::string (a), std::string (b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of the compare operator for std::string vs. const char *
|
||||
*/
|
||||
inline bool equals (const std::string &a, const char *b)
|
||||
{
|
||||
return equals (a, std::string (b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A specialization of the compare operator for std::string vs. const char *
|
||||
*/
|
||||
inline bool equals (const char *a, const std::string &b)
|
||||
{
|
||||
return equals (std::string (a), b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The base class for tests
|
||||
*/
|
||||
struct UT_PUBLIC TestBase
|
||||
{
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
TestBase (const std::string &file, const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~TestBase () { }
|
||||
|
||||
/**
|
||||
* @brief Actually runs the test
|
||||
* @return True, if the test was successful
|
||||
*/
|
||||
bool do_test (bool editable, bool slow);
|
||||
|
||||
/**
|
||||
* @brief Indicates that the test is a long runner
|
||||
* In this case the test is skipped unless in slow mode.
|
||||
*/
|
||||
void test_is_long_runner ();
|
||||
|
||||
/**
|
||||
* @brief Indicates that the test is a test that should run in editable mode only.
|
||||
* In this case the test is skipped unless in editable mode.
|
||||
*/
|
||||
void test_is_editable_only ();
|
||||
|
||||
/**
|
||||
* @brief Indicates that the test is a test that should run in non-editable mode only.
|
||||
* In this case the test is skipped unless in non-editable mode.
|
||||
*/
|
||||
void test_is_non_editable_only ();
|
||||
|
||||
/**
|
||||
* @brief Raises an exception with the given string
|
||||
* This version prints the last checkpoint for reference.
|
||||
*/
|
||||
void raise (const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Raises an exception with the given string, file and line number
|
||||
*/
|
||||
void raise (const std::string &file, int line, const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Registers a checkpoint
|
||||
*/
|
||||
void checkpoint (const std::string &file, int line);
|
||||
|
||||
/**
|
||||
* @brief Resets the checkpoints set
|
||||
*/
|
||||
void reset_checkpoint ();
|
||||
|
||||
/**
|
||||
* @brief Compares a layout with a golden layout file
|
||||
* @param layout The layout to compare
|
||||
* @param au_file The golden file path
|
||||
* @param norm The normalization mode (see NormalizationMode)
|
||||
* @param tolerance A tolerance applied when comparing shapes in database units
|
||||
* The layout is normalized by writing to a file and reading back
|
||||
*/
|
||||
void compare_layouts (const db::Layout &layout, const std::string &au_file, NormalizationMode norm = WriteGDS2, db::Coord tolerance = 0);
|
||||
|
||||
/**
|
||||
* @brief Compares a layout with a golden layout file with layer mapping
|
||||
* @param layout The layout to compare
|
||||
* @param au_file The golden file path
|
||||
* @param lmap The layer mapping object
|
||||
* @param read_all_others If true, all other layers will be read too
|
||||
* @param norm The normalization mode (see NormalizationMode)
|
||||
* @param tolerance A tolerance applied when comparing shapes in database units
|
||||
* The layout is normalized by writing to a file and reading back
|
||||
*/
|
||||
void compare_layouts (const db::Layout &layout, const std::string &au_file, const db::LayerMap &lmap, bool read_all_others, NormalizationMode norm = WriteGDS2, db::Coord tolerance = 0);
|
||||
|
||||
/**
|
||||
* @brief Compares two text files
|
||||
*/
|
||||
void compare_text_files (const std::string &path_a, const std::string &path_b);
|
||||
|
||||
/**
|
||||
* @brief The test's name
|
||||
* @return The name of the test
|
||||
*/
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_test;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares a temporary file path
|
||||
* @param fn The actual name of the file
|
||||
* @return A path suitable for writing a temporary file
|
||||
* The directory for the file will be created within this method.
|
||||
*/
|
||||
std::string tmp_file (const std::string &fn = "tmp") const;
|
||||
|
||||
/**
|
||||
* @brief Removes all temporary files
|
||||
*/
|
||||
void remove_tmp_folder ();
|
||||
|
||||
/**
|
||||
* @brief A generic diff printer
|
||||
*/
|
||||
template <class X, class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, const X &subject, const Y & /*ref*/)
|
||||
{
|
||||
std::ostringstream sstr;
|
||||
sstr << msg << " (actual value is " << subject << ")";
|
||||
raise (file, line, sstr.str ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A generic diff printer
|
||||
*/
|
||||
template <class X, class Y>
|
||||
void detailed_diff (const std::string &file, int line, const std::string &msg, const X &subject, const Y &ref)
|
||||
{
|
||||
std::ostringstream sstr;
|
||||
sstr << msg << std::endl;
|
||||
write_detailed_diff (sstr, tl::to_string (subject), tl::to_string (ref));
|
||||
raise (file, line, sstr.str ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for int vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, int subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for unsigned int vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, unsigned int subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for long vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, long subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for unsigned long vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, unsigned long subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for long long vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, long long subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for unsigned long long vs. something
|
||||
*/
|
||||
template <class Y>
|
||||
void diff (const std::string &file, int line, const std::string &msg, unsigned long long subject, const Y &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for bool
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, bool subject, bool ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for double
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, double subject, double ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for strings
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, const std::string &subject, const std::string &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for strings vs. const char *
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, const std::string &subject, const char *ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for strings vs. const char *
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, const char *subject, const std::string &ref)
|
||||
{
|
||||
detailed_diff (file, line, msg, subject, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A diff printer for C strings
|
||||
*/
|
||||
inline void diff (const std::string &file, int line, const std::string &msg, const char *subject, const char *ref)
|
||||
{
|
||||
diff (file, line, msg, std::string (subject), std::string (ref));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main entry point for the compare feature (EXPECT_EQ and EXPECT_NE)
|
||||
*/
|
||||
template <class T1, class T2>
|
||||
void eq_helper (bool eq, const T1 &a, const T2 &b, const char *what_expr, const char *equals_expr, const char *file, int line)
|
||||
{
|
||||
if (ut::equals (a, b) != eq) {
|
||||
std::ostringstream sstr;
|
||||
sstr << what_expr << " does not equal " << equals_expr;
|
||||
diff (file, line, sstr.str (), a, b);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void execute (ut::TestBase *_this) throw (tl::Exception) = 0;
|
||||
|
||||
void write_detailed_diff (std::ostream &os, const std::string &subject, const std::string &ref);
|
||||
|
||||
bool m_editable, m_slow;
|
||||
std::string m_test;
|
||||
std::string m_testdir;
|
||||
// last checkpoint
|
||||
std::string m_cp_file;
|
||||
int m_cp_line;
|
||||
bool m_any_failed;
|
||||
QString m_testtmp;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The registration facility for tests
|
||||
*/
|
||||
struct Registrar
|
||||
{
|
||||
static void reg (ut::TestBase *t)
|
||||
{
|
||||
if (! ms_instance) {
|
||||
ms_instance = new Registrar ();
|
||||
}
|
||||
ms_instance->m_tests.push_back (t);
|
||||
}
|
||||
|
||||
static Registrar *instance ()
|
||||
{
|
||||
return ms_instance;
|
||||
}
|
||||
|
||||
const std::vector <ut::TestBase *> &tests () const
|
||||
{
|
||||
return m_tests;
|
||||
}
|
||||
|
||||
private:
|
||||
static Registrar *ms_instance;
|
||||
|
||||
Registrar () : m_tests () { }
|
||||
|
||||
std::vector <ut::TestBase *> m_tests;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "utTestConsole.h"
|
||||
#include "utHead.h"
|
||||
|
||||
#include "rba.h"
|
||||
#include "pya.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# include <sys/ioctl.h>
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace ut
|
||||
{
|
||||
|
||||
// ------------------------------------------------
|
||||
// CaptureChannel implementation
|
||||
|
||||
CaptureChannel::CaptureChannel ()
|
||||
{
|
||||
tl::info.add (this, false);
|
||||
tl::error.add (this, false);
|
||||
tl::warn.add (this, false);
|
||||
}
|
||||
|
||||
void CaptureChannel::puts (const char *s)
|
||||
{
|
||||
m_text << s;
|
||||
}
|
||||
|
||||
void CaptureChannel::endl ()
|
||||
{
|
||||
m_text << "\n";
|
||||
}
|
||||
|
||||
void CaptureChannel::end ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void CaptureChannel::begin ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
// tl::Channel implementations for redirecting the log output
|
||||
|
||||
class InfoChannel : public tl::Channel
|
||||
{
|
||||
public:
|
||||
InfoChannel (int verbosity)
|
||||
: m_verbosity (verbosity)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void puts (const char *s)
|
||||
{
|
||||
if (tl::verbosity () >= m_verbosity) {
|
||||
TestConsole::instance ()->basic_write (s);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void endl ()
|
||||
{
|
||||
if (tl::verbosity () >= m_verbosity) {
|
||||
TestConsole::instance ()->basic_write ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end ()
|
||||
{
|
||||
TestConsole::instance ()->flush ();
|
||||
}
|
||||
|
||||
virtual void begin ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
private:
|
||||
int m_verbosity;
|
||||
};
|
||||
|
||||
class WarningChannel : public tl::Channel
|
||||
{
|
||||
public:
|
||||
WarningChannel ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void puts (const char *s)
|
||||
{
|
||||
TestConsole::instance ()->basic_write (s);
|
||||
}
|
||||
|
||||
virtual void endl ()
|
||||
{
|
||||
TestConsole::instance ()->basic_write ("\n");
|
||||
}
|
||||
|
||||
virtual void end ()
|
||||
{
|
||||
TestConsole::instance ()->end ();
|
||||
TestConsole::instance ()->flush ();
|
||||
}
|
||||
|
||||
virtual void begin ()
|
||||
{
|
||||
TestConsole::instance ()->begin_warn ();
|
||||
}
|
||||
};
|
||||
|
||||
class ErrorChannel : public tl::Channel
|
||||
{
|
||||
public:
|
||||
ErrorChannel ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void puts (const char *s)
|
||||
{
|
||||
TestConsole::instance ()->basic_write (s);
|
||||
}
|
||||
|
||||
virtual void endl ()
|
||||
{
|
||||
TestConsole::instance ()->basic_write ("\n");
|
||||
}
|
||||
|
||||
virtual void end ()
|
||||
{
|
||||
TestConsole::instance ()->end ();
|
||||
TestConsole::instance ()->flush ();
|
||||
}
|
||||
|
||||
virtual void begin ()
|
||||
{
|
||||
TestConsole::instance ()->begin_error ();
|
||||
}
|
||||
};
|
||||
|
||||
class CtrlChannel : public tl::Channel
|
||||
{
|
||||
public:
|
||||
CtrlChannel (bool with_xml)
|
||||
: m_with_xml (with_xml)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void puts (const char *s)
|
||||
{
|
||||
if (m_with_xml == TestConsole::instance ()->xml_format ()) {
|
||||
TestConsole::instance ()->raw_write (s);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void endl ()
|
||||
{
|
||||
if (m_with_xml == TestConsole::instance ()->xml_format ()) {
|
||||
TestConsole::instance ()->raw_write ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end ()
|
||||
{
|
||||
if (m_with_xml == TestConsole::instance ()->xml_format ()) {
|
||||
TestConsole::instance ()->end ();
|
||||
TestConsole::instance ()->flush ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void begin ()
|
||||
{
|
||||
if (m_with_xml == TestConsole::instance ()->xml_format ()) {
|
||||
TestConsole::instance ()->begin_info ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_with_xml;
|
||||
};
|
||||
|
||||
// ------------------------------------------------
|
||||
// TestConsole implementation
|
||||
|
||||
const char *ANSI_RED = "\033[31;1m";
|
||||
const char *ANSI_BLUE = "\033[34m";
|
||||
const char *ANSI_GREEN = "\033[32m";
|
||||
const char *ANSI_RESET = "\033[0m";
|
||||
|
||||
TestConsole::TestConsole (FILE *file, bool xml_format)
|
||||
: m_file (file), m_xml_format (xml_format), m_col (0), m_max_col (250), m_columns (50), m_rows (0), m_is_tty (false)
|
||||
{
|
||||
ms_instance = this;
|
||||
m_indent = 4;
|
||||
|
||||
m_is_tty = isatty (fileno (file)) && ! xml_format;
|
||||
|
||||
#if !defined(_WIN32)
|
||||
if (m_is_tty) {
|
||||
struct winsize ws;
|
||||
ioctl (fileno (stdout), TIOCGWINSZ, &ws);
|
||||
m_columns = std::max (0, (int) ws.ws_col);
|
||||
m_rows = std::max (0, (int) ws.ws_row);
|
||||
}
|
||||
#endif
|
||||
|
||||
redirect ();
|
||||
}
|
||||
|
||||
TestConsole::~TestConsole ()
|
||||
{
|
||||
restore ();
|
||||
|
||||
if (ms_instance == this) {
|
||||
ms_instance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestConsole::write_str (const char *text, output_stream os)
|
||||
{
|
||||
if (os == OS_stderr) {
|
||||
begin_error ();
|
||||
basic_write (text);
|
||||
end ();
|
||||
} else {
|
||||
basic_write (text);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestConsole::raw_write (const char *text)
|
||||
{
|
||||
fputs (text, m_file);
|
||||
}
|
||||
|
||||
void
|
||||
TestConsole::flush ()
|
||||
{
|
||||
fflush (m_file);
|
||||
}
|
||||
|
||||
void
|
||||
TestConsole::begin_error ()
|
||||
{
|
||||
if (m_is_tty) {
|
||||
fputs (ANSI_RED, m_file);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestConsole::begin_info ()
|
||||
{
|
||||
if (m_is_tty) {
|
||||
fputs (ANSI_GREEN, m_file);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestConsole::begin_warn ()
|
||||
{
|
||||
if (m_is_tty) {
|
||||
fputs (ANSI_BLUE, m_file);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestConsole::end ()
|
||||
{
|
||||
if (m_is_tty) {
|
||||
fputs (ANSI_RESET, m_file);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestConsole::basic_write (const char *s)
|
||||
{
|
||||
if (m_xml_format) {
|
||||
|
||||
for (const char *cp = s; *cp; ++cp) {
|
||||
if (*cp == '&') {
|
||||
fputs ("&", m_file);
|
||||
} else if (*cp == '<') {
|
||||
fputs ("<", m_file);
|
||||
} else if (*cp == '>') {
|
||||
fputs (">", m_file);
|
||||
} else {
|
||||
fputc (*cp, m_file);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// line length limitation - this assumes we are always printing to the same terminal
|
||||
// or we don't mix stderr/stdout.
|
||||
const char *cp;
|
||||
for (cp = s; *cp; ++cp) {
|
||||
if (*cp == '\n' || *cp == '\r') {
|
||||
m_col = 0;
|
||||
fputc (*cp, m_file);
|
||||
} else {
|
||||
if (m_col == 0) {
|
||||
for (int i = 0; i < m_indent; ++i) {
|
||||
fputc (' ', m_file);
|
||||
}
|
||||
m_col = m_indent;
|
||||
}
|
||||
if (m_col > m_max_col) {
|
||||
// ignore char
|
||||
} else if (m_col == m_max_col) {
|
||||
fputs (" ...", m_file);
|
||||
++m_col;
|
||||
} else if (*cp == '\033') {
|
||||
// skip ANSI escape sequences (no increment of s_col)
|
||||
const char *cpend = cp + 1;
|
||||
if (*cpend == '[') {
|
||||
++cpend;
|
||||
while (*cpend && *cpend != 'm') {
|
||||
++cpend;
|
||||
}
|
||||
if (*cpend) {
|
||||
++cpend;
|
||||
}
|
||||
}
|
||||
while (cp != cpend) {
|
||||
fputc (*cp++, m_file);
|
||||
}
|
||||
--cp;
|
||||
} else {
|
||||
fputc (*cp, m_file);
|
||||
++m_col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestConsole::redirect ()
|
||||
{
|
||||
// redirect the log channels
|
||||
tl::warn.clear ();
|
||||
tl::warn.add (new ut::WarningChannel (), true);
|
||||
tl::info.clear ();
|
||||
tl::info.add (new ut::InfoChannel (0), true);
|
||||
tl::log.clear ();
|
||||
tl::log.add (new ut::InfoChannel (10), true);
|
||||
tl::error.clear ();
|
||||
tl::error.add (new ut::ErrorChannel (), true);
|
||||
|
||||
ut::ruby_interpreter ()->push_console (this);
|
||||
ut::python_interpreter ()->push_console (this);
|
||||
}
|
||||
|
||||
void
|
||||
TestConsole::restore ()
|
||||
{
|
||||
// TODO: we should basically restore the original channels
|
||||
tl::warn.clear ();
|
||||
tl::info.clear ();
|
||||
tl::log.clear ();
|
||||
tl::error.clear ();
|
||||
|
||||
ut::ruby_interpreter ()->remove_console (this);
|
||||
ut::python_interpreter ()->remove_console (this);
|
||||
}
|
||||
|
||||
TestConsole *TestConsole::ms_instance = 0;
|
||||
|
||||
tl::LogTee noctrl (new CtrlChannel (false), true);
|
||||
tl::LogTee ctrl (new CtrlChannel (true), true);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_utTestConsole
|
||||
#define HDR_utTestConsole
|
||||
|
||||
#include "utCommon.h"
|
||||
#include "tlLog.h"
|
||||
#include "gsiInterpreter.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
|
||||
namespace ut
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A utility class to capture the warning, error and info channels
|
||||
*
|
||||
* Instantiate this class inside a test. Then run the test and finally
|
||||
* obtain the collected output with CaptureChannel::captured_text().
|
||||
*/
|
||||
class UT_PUBLIC CaptureChannel : public tl::Channel
|
||||
{
|
||||
public:
|
||||
CaptureChannel ();
|
||||
|
||||
std::string captured_text () const
|
||||
{
|
||||
return m_text.str ();
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
m_text.str (std::string ());
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void puts (const char *s);
|
||||
virtual void endl ();
|
||||
virtual void end ();
|
||||
virtual void begin ();
|
||||
|
||||
private:
|
||||
std::ostringstream m_text;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Redirects the interpreter output and serves as a general output device
|
||||
*/
|
||||
class TestConsole
|
||||
: public gsi::Console
|
||||
{
|
||||
public:
|
||||
static TestConsole *instance ()
|
||||
{
|
||||
tl_assert (ms_instance != 0);
|
||||
return ms_instance;
|
||||
}
|
||||
|
||||
TestConsole (FILE *file, bool xml_format);
|
||||
~TestConsole ();
|
||||
|
||||
int indent () const
|
||||
{
|
||||
return m_indent;
|
||||
}
|
||||
|
||||
bool xml_format () const
|
||||
{
|
||||
return m_xml_format;
|
||||
}
|
||||
|
||||
void write_str (const char *text, output_stream os);
|
||||
void raw_write (const char *text);
|
||||
virtual void flush ();
|
||||
|
||||
virtual bool is_tty ()
|
||||
{
|
||||
// NOTE: this assumes we are delivering to stdout
|
||||
return m_is_tty;
|
||||
}
|
||||
|
||||
virtual int columns ()
|
||||
{
|
||||
return std::max (m_columns - m_indent, 0);
|
||||
}
|
||||
|
||||
virtual int rows ()
|
||||
{
|
||||
return m_rows;
|
||||
}
|
||||
|
||||
int real_columns ()
|
||||
{
|
||||
return m_columns;
|
||||
}
|
||||
|
||||
void begin_error ();
|
||||
void begin_info ();
|
||||
void begin_warn ();
|
||||
void end ();
|
||||
void basic_write (const char *s);
|
||||
|
||||
private:
|
||||
FILE *m_file;
|
||||
bool m_xml_format;
|
||||
int m_col;
|
||||
int m_max_col;
|
||||
int m_columns, m_rows;
|
||||
bool m_is_tty;
|
||||
int m_indent;
|
||||
static TestConsole *ms_instance;
|
||||
|
||||
void redirect ();
|
||||
void restore ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
dbu 0.001
|
||||
|
||||
target($drc_test_target, "TOP")
|
||||
|
||||
x = polygon_layer
|
||||
x.is_empty? == true || raise("unexpected value")
|
||||
x.is_box? == false || raise("unexpected value")
|
||||
x.insert(box(4.0, 0, 4.7, 0.7))
|
||||
x.is_empty? == false || raise("unexpected value")
|
||||
x.is_box? == true || raise("unexpected value")
|
||||
x.insert(polygon([ p(0, 0), p(2.0, 0), p(1.0, 1.0) ]))
|
||||
x.insert(polygon([ p(0, -5.0), p(2.0, -5.0), p(1.0, -6.0) ]))
|
||||
x.insert(path([ p(0, -2), p(2.0, -2) ], 0.2))
|
||||
x.is_box? == false || raise("unexpected value")
|
||||
x.output(10, 0)
|
||||
|
||||
y = polygon_layer
|
||||
y.insert(polygon([ p(0, 0), p(0, 1.0), p(2.0, 1.0), p(2.0, 2.0), p(1.0, 2.0), p(1.0, 0) ]))
|
||||
y.output(11, 0)
|
||||
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
|
||||
target($drc_test_target, "TOP")
|
||||
source($drc_test_source, "TOP")
|
||||
|
||||
a1 = input(1)
|
||||
b1 = input(2)
|
||||
c1 = input(3)
|
||||
|
||||
a1.output(1, 0)
|
||||
b1.output(2, 0)
|
||||
c1.output(3, 0)
|
||||
|
||||
c1.rounded_corners(0.5, 0.5, 16).output(1010, 0)
|
||||
c1.smoothed(1.5).output(1011, 0)
|
||||
|
||||
a1.texts.output(1020, 0)
|
||||
a1.texts("A*").output(1021, 0)
|
||||
a1.texts(text("A*")).output(1022, 0)
|
||||
a1.texts(pattern("A*")).output(1023, 0)
|
||||
a1.texts(pattern("A*"), as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1024, 0)
|
||||
a1.texts(pattern("A*"), as_boxes).output(1025, 0)
|
||||
|
||||
a1.middle.sized(0.05).output(1030, 0)
|
||||
a1.middle(as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1031, 0)
|
||||
a1.middle(as_boxes).sized(0.05).output(1032, 0)
|
||||
a1.extent_refs(0.5, 0.5).sized(0.05).output(1040, 0)
|
||||
a1.extent_refs(:center).sized(0.05).output(1040, 1)
|
||||
a1.extent_refs(:c).sized(0.05).output(1040, 2)
|
||||
a1.extent_refs(:bottom).sized(0.05).output(1041, 0)
|
||||
a1.extent_refs(:b).sized(0.05).output(1041, 1)
|
||||
a1.extent_refs(:b, as_edges).extended(0.05, 0.05, 0.05, 0.05).output(1041, 2)
|
||||
a1.extent_refs(:top).sized(0.05).output(1042, 0)
|
||||
a1.extent_refs(:t).sized(0.05).output(1042, 1)
|
||||
a1.extent_refs(:left).sized(0.05).output(1043, 0)
|
||||
a1.extent_refs(:l).sized(0.05).output(1043, 1)
|
||||
a1.extent_refs(:right).sized(0.05).output(1044, 0)
|
||||
a1.extent_refs(:r).sized(0.05).output(1044, 1)
|
||||
a1.extent_refs(:bottom_left).sized(0.05).output(1045, 0)
|
||||
a1.extent_refs(:bl).sized(0.05).output(1045, 1)
|
||||
a1.extent_refs(:bottom_center).sized(0.05).output(1046, 0)
|
||||
a1.extent_refs(:bc).sized(0.05).output(1046, 1)
|
||||
a1.extent_refs(:bottom_right).sized(0.05).output(1047, 0)
|
||||
a1.extent_refs(:br).sized(0.05).output(1047, 1)
|
||||
a1.extent_refs(:top_left).sized(0.05).output(1048, 0)
|
||||
a1.extent_refs(:tl).sized(0.05).output(1048, 1)
|
||||
a1.extent_refs(:top_center).sized(0.05).output(1049, 0)
|
||||
a1.extent_refs(:tc).sized(0.05).output(1049, 1)
|
||||
a1.extent_refs(:top_right).sized(0.05).output(1050, 0)
|
||||
a1.extent_refs(:tr).sized(0.05).output(1050, 1)
|
||||
a1.extent_refs(:left_center).sized(0.05).output(1051, 0)
|
||||
a1.extent_refs(:lc).sized(0.05).output(1051, 1)
|
||||
a1.extent_refs(:right_center).sized(0.05).output(1052, 0)
|
||||
a1.extent_refs(:rc).sized(0.05).output(1052, 1)
|
||||
a1.extent_refs(0.25, 0.5, 0.5, 0.75).output(1053, 0)
|
||||
|
||||
a1.corners.sized(0.05).output(1060, 0)
|
||||
a1.corners(-90.0, as_boxes).sized(0.05).output(1061, 0)
|
||||
a1.corners(-90.0, as_dots).extended(0.05, 0.05, 0.05, 0.05).output(1062, 0)
|
||||
|
||||
a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0)
|
||||
a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0)
|
||||
a1.collect_to_region { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1102, 0)
|
||||
a1.collect_to_edges { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1103, 0)
|
||||
a1.collect { |p| p.is_box? && p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) }.output(1104, 0)
|
||||
a1.collect { |p| p.is_box? && [ p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(150, 150), p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) ] }.output(1105, 0)
|
||||
lx = polygon_layer
|
||||
a1.each { |p| p.is_box? && lx.insert(p) }
|
||||
lx.output(1106, 0)
|
||||
a1.collect { |p| p.is_box? && RBA::Region::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))).sized(120) }.output(1107, 0)
|
||||
a1.collect { |p| p.is_box? && RBA::Polygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1108, 0)
|
||||
a1.collect { |p| p.is_box? && RBA::DPolygon::new(p.bbox) }.output(1109, 0)
|
||||
a1.collect { |p| p.is_box? && RBA::SimplePolygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1110, 0)
|
||||
a1.collect { |p| p.is_box? && RBA::DSimplePolygon::new(p.bbox) }.output(1111, 0)
|
||||
a1.collect_to_edges { |p| p.is_box? && p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) }.output(1112, 0)
|
||||
a1.collect_to_edges { |p| p.is_box? && [ p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(150, 150), p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(120, 120) ] }.output(1113, 0)
|
||||
a1.collect_to_edges { |p| p.is_box? && RBA::Region::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))).sized(120) }.output(1114, 0)
|
||||
a1.collect_to_edges { |p| p.is_box? && RBA::Polygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1115, 0)
|
||||
a1.collect_to_edges { |p| p.is_box? && RBA::DPolygon::new(p.bbox) }.output(1116, 0)
|
||||
a1.collect_to_edges { |p| p.is_box? && RBA::SimplePolygon::new(p.bbox.transformed(RBA::VCplxTrans::new(1000.0))) }.output(1117, 0)
|
||||
a1.collect_to_edges { |p| p.is_box? && RBA::DSimplePolygon::new(p.bbox) }.output(1118, 0)
|
||||
|
||||
a1.edges.select { |p| p.length < 0.8 }.output(1120, 0)
|
||||
a1.edges.collect { |p| p.length < 0.8 && p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0)
|
||||
a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.enlarged(0.1, 0.1) }.output(1122, 0)
|
||||
a1.edges.collect_to_region { |p| p.length < 0.8 && p.bbox.transformed(RBA::VCplxTrans::new(1000.0)).enlarged(100, 100) }.output(1123, 0)
|
||||
|
||||
# edge pair collect
|
||||
a1.width(1.5).collect { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1120, 0)
|
||||
a1.width(1.5).collect_to_edge_pairs { |p| p.transformed(RBA::VCplxTrans::new(1000.0)) }.output(1121, 0)
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
# Foreign cell test
|
||||
|
||||
source($drc_test_source, "TOPTOP")
|
||||
target($drc_test_target)
|
||||
|
||||
cell("TOP")
|
||||
|
||||
a1 = input(1)
|
||||
b1 = input(2)
|
||||
|
||||
output_cell("NEW")
|
||||
|
||||
a1.and(b1).output(1000, 0)
|
||||
|
||||
cell("TOPTOP")
|
||||
|
||||
a1.and(b1).output(1001, 0)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,494 @@
|
|||
|
||||
def message(m)
|
||||
$stdout.puts(m)
|
||||
$stdout.flush
|
||||
end
|
||||
|
||||
def run_testsuite(dm, ic, tiled = false)
|
||||
|
||||
lb = 100
|
||||
|
||||
a = input(RBA::LayerInfo::new(1, 0))
|
||||
b = input(2)
|
||||
c = input(3)
|
||||
x = input(10)
|
||||
y = input(11)
|
||||
|
||||
h = {}
|
||||
layers.each { |l| h[l] = true }
|
||||
h[RBA::LayerInfo::new(1, 0)] || raise("missing layer 1/0 in layers list")
|
||||
|
||||
c.is_merged? == false || raise("unexpected value")
|
||||
|
||||
message "--- general #{lb}"
|
||||
|
||||
l1 = a&b
|
||||
l1.output(lb, dm)
|
||||
l1.is_empty? && raise("must not be empty")
|
||||
|
||||
a.and(b).xor(l1).is_empty? || raise("xor not empty")
|
||||
tiled || (a.and(b).is_merged? == true || raise("unexpected value"))
|
||||
|
||||
a.xor(b).output(RBA::LayerInfo::new(lb + 1, dm))
|
||||
a.xor(b).xor(a ^ b).is_empty? || raise("xor not empty")
|
||||
a.not(b).output(lb + 2, dm)
|
||||
a.not(b).xor(a - b).is_empty? || raise("xor not empty")
|
||||
a.or(b).output(lb + 3, dm)
|
||||
a.or(b).xor(a | b).is_empty? || raise("xor not empty")
|
||||
|
||||
a.join(b).output(lb + 4, dm)
|
||||
a.join(b).xor(a + b).is_empty? || raise("xor not empty")
|
||||
|
||||
if !tiled
|
||||
a.join(b).data.size == 16 * ic || raise("unexpected shape count")
|
||||
end
|
||||
|
||||
c.raw.is_clean? == false || raise("unexpected value")
|
||||
# c.raw switched the semantics
|
||||
c.is_clean? == false || raise("unexpected value")
|
||||
(c.area / ic).to_s == "10.0" || raise("unexpected value")
|
||||
(c.edges.length / ic).to_s == "18.0" || raise("unexpected value")
|
||||
(c.perimeter / ic).to_s == "18.0" || raise("unexpected value")
|
||||
c.clean
|
||||
c.is_clean? == true || raise("unexpected value")
|
||||
(c.area / ic).to_s == "9.0" || raise("unexpected value")
|
||||
(c.edges.length / ic).to_s == "14.0" || raise("unexpected value")
|
||||
(c.perimeter / ic).to_s == "14.0" || raise("unexpected value")
|
||||
(c.dup.area / ic).to_s == "9.0" || raise("unexpected value")
|
||||
(c.raw.clean.area / ic).to_s == "9.0" || raise("unexpected value")
|
||||
(c.area / ic).to_s == "9.0" || raise("unexpected value")
|
||||
(c.raw.area / ic).to_s == "10.0" || raise("unexpected value")
|
||||
c.clean
|
||||
|
||||
if ic == 1
|
||||
c.bbox.to_s == "(-4,-5;0,-2)" || raise("unexpected value")
|
||||
end
|
||||
|
||||
lb += 10
|
||||
message "--- centers, start_segments, end_segments #{lb}"
|
||||
|
||||
a.edges.centers(0.1).extended_out(0.01).output(lb, dm)
|
||||
a.edges.centers(0, 0.5).extended_out(0.01).output(lb + 1, dm)
|
||||
a.edges.centers(0.5, 0.5).extended_out(0.01).output(lb + 2, dm)
|
||||
a.edges.start_segments(0.1).extended_out(0.01).output(lb + 3, dm)
|
||||
a.edges.start_segments(0, 0.5).extended_out(0.01).output(lb + 4, dm)
|
||||
a.edges.start_segments(0.5, 0.5).extended_out(0.01).output(lb + 5, dm)
|
||||
a.edges.end_segments(0.1).extended_out(0.01).output(lb + 6, dm)
|
||||
a.edges.end_segments(0, 0.5).extended_out(0.01).output(lb + 7, dm)
|
||||
a.edges.end_segments(0.5, 0.5).extended_out(0.01).output(lb + 8, dm)
|
||||
|
||||
a.polygons? == true || raise("unexpected value")
|
||||
a.edges? == false || raise("unexpected value")
|
||||
a.edge_pairs? == false || raise("unexpected value")
|
||||
a.edges.edge_pairs? == false || raise("unexpected value")
|
||||
a.edges.polygons? == false || raise("unexpected value")
|
||||
a.edges.edges? == true || raise("unexpected value")
|
||||
a.space(0.5).edge_pairs? == true || raise("unexpected value")
|
||||
a.space(0.5).polygons? == false || raise("unexpected value")
|
||||
a.space(0.5).edges? == false || raise("unexpected value")
|
||||
|
||||
lb += 10
|
||||
message "--- extended #{lb}"
|
||||
|
||||
a.edges.with_length(0.6).extended(begin: 0.1, end: 0.15, out: 0.1, in: -0.05).output(lb, dm)
|
||||
a.edges.with_length(0.6).extended(out: 0.1, in: -0.05).output(lb + 1, dm)
|
||||
a.edges.with_length(0..0.7).extended(out: 0.1, in: -0.05).output(lb + 2, dm)
|
||||
a.edges.with_length(0..0.7).extended(out: 0.1, in: -0.05, joined: true).output(lb + 3, dm)
|
||||
a.edges.with_length(0..0.7).extended(begin: 0.1, end: 0.15, out: 0.1, in: 0.05, joined: true).output(lb + 4, dm)
|
||||
a.edges.with_length(0..0.7).extended(0.1, 0.15, 0.1, 0.05).output(lb + 5, dm)
|
||||
a.edges.with_length(0..0.7).extended(0.1, 0.15, 0.1, 0.05, true).output(lb + 6, dm)
|
||||
a.edges.with_length(0..0.7).extended_out(0.1).output(lb + 7, dm)
|
||||
a.edges.with_length(0..0.7).extended_in(0.1).output(lb + 8, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- extents #{lb}"
|
||||
|
||||
b.extents.output(lb, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- first_edges, second_edges, edges #{lb}"
|
||||
|
||||
a.enclosing(b, 0.5, whole_edges).first_edges.extended_out(0.01).output(lb, dm)
|
||||
a.enclosing(b, 0.5, whole_edges).second_edges.extended_out(0.01).output(lb + 1, dm)
|
||||
a.enclosing(b, 0.5, whole_edges).edges.extended_out(0.01).output(lb + 2, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- hulls, holes #{lb}"
|
||||
|
||||
c.xor(b).holes.output(lb, dm)
|
||||
c.xor(b).hulls.output(lb + 1, dm)
|
||||
c.xor(b).xor(c.xor(b).hulls - c.xor(b).holes).is_empty? || raise("xor not empty")
|
||||
|
||||
lb += 10
|
||||
message "--- interacting, in, not_in #{lb}"
|
||||
|
||||
b.interacting(a).output(lb, dm)
|
||||
b.interacting(a).in(b).output(lb + 1, dm)
|
||||
(b|a).not_in(b).output(lb + 2, dm)
|
||||
x.in(b).output(lb + 3, dm)
|
||||
b.sized(0.1).in(b).is_empty? == true || raise("unexpected value")
|
||||
|
||||
lb += 10
|
||||
message "--- inside, outside, overlapping, interacting #{lb}"
|
||||
|
||||
b.inside(c).output(lb, dm)
|
||||
b.outside(c).output(lb + 1, dm)
|
||||
b.overlapping(c).output(lb + 2, dm)
|
||||
b.interacting(c).output(lb + 3, dm)
|
||||
bdup = b.dup
|
||||
bdup.select_inside(c)
|
||||
bdup.xor(b.inside(c)).output(lb + 4, dm)
|
||||
bdup = b.dup
|
||||
bdup.select_outside(c)
|
||||
bdup.xor(b.outside(c)).output(lb + 5, dm)
|
||||
bdup = b.dup
|
||||
bdup.select_overlapping(c)
|
||||
bdup.xor(b.overlapping(c)).output(lb + 6, dm)
|
||||
bdup = b.dup
|
||||
bdup.select_interacting(c)
|
||||
bdup.xor(b.interacting(c)).output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- merge #{lb}"
|
||||
|
||||
c.raw
|
||||
if !tiled
|
||||
c.merged.is_merged? == true || raise("unexpected value")
|
||||
end
|
||||
c.merged.output(lb, dm)
|
||||
c.merged(2).output(lb + 1, dm)
|
||||
c.merged(3).output(lb + 2, dm)
|
||||
c.clean
|
||||
cdup = c.dup
|
||||
cdup.merge.xor(c.merged).output(lb + 3, dm)
|
||||
cdup.xor(c.merged).output(lb + 4, dm)
|
||||
cdup = c.dup
|
||||
cdup.merge(2).xor(c.merged(2)).output(lb + 5, dm)
|
||||
cdup.xor(c.merged(2)).output(lb + 6, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- move, transform, rotate, scale #{lb}"
|
||||
|
||||
c.moved(0.2, -0.1).output(lb, dm)
|
||||
cdup = c.dup
|
||||
cdup.move(0.2, -0.1)
|
||||
cdup.xor(c.moved(0.2, -0.1)).is_empty? == true || raise("xor not empty")
|
||||
t = RBA::DCplxTrans::new(0.5)
|
||||
c.transformed(t).output(lb + 1, dm)
|
||||
cdup = c.dup
|
||||
cdup.transform(t)
|
||||
cdup.xor(c.transformed(t)).is_empty? == true || raise("xor not empty")
|
||||
c.scaled(0.5).output(lb + 2, dm)
|
||||
cdup = c.dup
|
||||
cdup.scale(0.5)
|
||||
cdup.xor(c.scaled(0.5)).is_empty? == true || raise("xor not empty")
|
||||
c.rotated(45).output(lb + 3, dm)
|
||||
cdup = c.dup
|
||||
cdup.rotate(45)
|
||||
cdup.xor(c.rotated(45)).is_empty? == true || raise("xor not empty")
|
||||
|
||||
lb += 10
|
||||
message "--- rectangles, rectilinear #{lb}"
|
||||
|
||||
a.rectangles.output(lb, dm)
|
||||
a.non_rectangles.output(lb + 1, dm)
|
||||
a.rectilinear.output(lb + 2, dm)
|
||||
a.non_rectilinear.output(lb + 3, dm)
|
||||
c.raw
|
||||
c.non_rectangles.output(lb + 4, dm)
|
||||
c.clean
|
||||
c.rectangles.output(lb + 5, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- enclosing #{lb}"
|
||||
|
||||
a.enclosing(b, 0.5).polygons.output(lb, dm)
|
||||
a.enclosing(b, 0.5).polygons.xor(a.enc(b, 0.5).polygons).is_empty? == true || raise("xor not empty")
|
||||
a.enclosing(b, 0.5, euclidian).polygons.output(lb + 1, dm)
|
||||
a.enclosing(b, 0.5, square).polygons.output(lb + 2, dm)
|
||||
a.enclosing(b, 0.5, projection).polygons.output(lb + 3, dm)
|
||||
a.enclosing(b, 0.5, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
a.enclosing(b, 0.5, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
a.enclosing(b, 0.5, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
a.enclosing(b, 0.5, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- enclosing (edges) #{lb}"
|
||||
|
||||
ae = a.edges
|
||||
be = b.edges
|
||||
ae.enclosing(be, 0.5).polygons.output(lb, dm)
|
||||
ae.enclosing(be, 0.5).polygons.xor(ae.enc(be, 0.5).polygons).is_empty? == true || raise("xor not empty")
|
||||
ae.enclosing(be, 0.5, euclidian).polygons.output(lb + 1, dm)
|
||||
ae.enclosing(be, 0.5, square).polygons.output(lb + 2, dm)
|
||||
ae.enclosing(be, 0.5, projection).polygons.output(lb + 3, dm)
|
||||
ae.enclosing(be, 0.5, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
ae.enclosing(be, 0.5, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
ae.enclosing(be, 0.5, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
ae.enclosing(be, 0.5, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- isolated #{lb}"
|
||||
|
||||
b.isolated(0.4).polygons.output(lb, dm)
|
||||
b.isolated(0.4).polygons.xor(b.iso(0.4).polygons).is_empty? == true || raise("xor not empty")
|
||||
b.isolated(0.4, euclidian).polygons.output(lb + 1, dm)
|
||||
b.isolated(0.4, square).polygons.output(lb + 2, dm)
|
||||
b.isolated(0.4, projection).polygons.output(lb + 3, dm)
|
||||
b.isolated(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
b.isolated(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
b.isolated(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
b.isolated(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- notch #{lb}"
|
||||
|
||||
b.notch(0.4).polygons.output(lb, dm)
|
||||
b.notch(0.4, euclidian).polygons.output(lb + 1, dm)
|
||||
b.notch(0.4, square).polygons.output(lb + 2, dm)
|
||||
b.notch(0.4, projection).polygons.output(lb + 3, dm)
|
||||
b.notch(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
b.notch(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
b.notch(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
b.notch(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- overlap #{lb}"
|
||||
|
||||
a.overlap(b, 0.5).polygons.output(lb, dm)
|
||||
a.overlap(b, 0.5, euclidian).polygons.output(lb + 1, dm)
|
||||
a.overlap(b, 0.5, square).polygons.output(lb + 2, dm)
|
||||
a.overlap(b, 0.5, projection).polygons.output(lb + 3, dm)
|
||||
a.overlap(b, 0.5, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
a.overlap(b, 0.5, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
a.overlap(b, 0.5, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
a.overlap(b, 0.5, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- overlap (edges) #{lb}"
|
||||
|
||||
ae = a.edges
|
||||
be = b.edges
|
||||
ae.overlap(be, 0.5).polygons.output(lb, dm)
|
||||
ae.overlap(be, 0.5, euclidian).polygons.output(lb + 1, dm)
|
||||
ae.overlap(be, 0.5, square).polygons.output(lb + 2, dm)
|
||||
ae.overlap(be, 0.5, projection).polygons.output(lb + 3, dm)
|
||||
ae.overlap(be, 0.5, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
ae.overlap(be, 0.5, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
ae.overlap(be, 0.5, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
ae.overlap(be, 0.5, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- separation #{lb}"
|
||||
|
||||
a.separation(b, 0.4).polygons.output(lb, dm)
|
||||
a.separation(b, 0.4).polygons.xor(a.sep(b, 0.4).polygons).is_empty? == true || raise("xor not empty")
|
||||
a.separation(b, 0.4, euclidian).polygons.output(lb + 1, dm)
|
||||
a.separation(b, 0.4, square).polygons.output(lb + 2, dm)
|
||||
a.separation(b, 0.4, projection).polygons.output(lb + 3, dm)
|
||||
a.separation(b, 0.4, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
a.separation(b, 0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
a.separation(b, 0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
a.separation(b, 0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- sized #{lb}"
|
||||
|
||||
x.sized(0.1).output(lb, dm)
|
||||
x.sized(0.1, 0).output(lb + 1, dm)
|
||||
x.sized(0.1, diamond_limit).output(lb + 2, dm)
|
||||
x.sized(0.1, octagon_limit).output(lb + 3, dm)
|
||||
x.sized(0.1, square_limit).output(lb + 4, dm)
|
||||
x.sized(0.1, acute_limit).output(lb + 5, dm)
|
||||
x.sized(0.1, no_limit).output(lb + 6, dm)
|
||||
xdup = x.dup
|
||||
xdup.size(0.1, 0)
|
||||
xdup.xor(x.sized(0.1, 0)).is_empty? == true || raise("xor not empty")
|
||||
a.sized(4.0, no_limit).output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- space #{lb}"
|
||||
|
||||
b.space(0.4).polygons.output(lb, dm)
|
||||
b.space(0.4, euclidian).polygons.output(lb + 1, dm)
|
||||
b.space(0.4, square).polygons.output(lb + 2, dm)
|
||||
b.space(0.4, projection).polygons.output(lb + 3, dm)
|
||||
b.space(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
b.space(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
b.space(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
b.space(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- space (edges) #{lb}"
|
||||
|
||||
be = b.edges
|
||||
be.space(0.4).polygons.output(lb, dm)
|
||||
be.space(0.4, euclidian).polygons.output(lb + 1, dm)
|
||||
be.space(0.4, square).polygons.output(lb + 2, dm)
|
||||
be.space(0.4, projection).polygons.output(lb + 3, dm)
|
||||
be.space(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
be.space(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
be.space(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
be.space(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- width #{lb}"
|
||||
|
||||
b.width(0.4).polygons.output(lb, dm)
|
||||
b.width(0.4, euclidian).polygons.output(lb + 1, dm)
|
||||
b.width(0.4, square).polygons.output(lb + 2, dm)
|
||||
b.width(0.4, projection).polygons.output(lb + 3, dm)
|
||||
b.width(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
b.width(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
b.width(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
b.width(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- width (edges) #{lb}"
|
||||
|
||||
be = b.edges
|
||||
be.width(0.4).polygons.output(lb, dm)
|
||||
be.width(0.4, euclidian).polygons.output(lb + 1, dm)
|
||||
be.width(0.4, square).polygons.output(lb + 2, dm)
|
||||
be.width(0.4, projection).polygons.output(lb + 3, dm)
|
||||
be.width(0.4, euclidian, whole_edges).polygons.output(lb + 4, dm)
|
||||
be.width(0.4, euclidian, projection_limits(0.4, nil)).polygons.output(lb + 5, dm)
|
||||
be.width(0.4, euclidian, projection_limits(nil, 0.4)).polygons.output(lb + 6, dm)
|
||||
be.width(0.4, euclidian, projection_limits(0..0.4)).polygons.output(lb + 7, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- with_angle #{lb}"
|
||||
|
||||
a.edges.with_angle(90.degree).extended_out(0.01).output(lb, dm)
|
||||
a.edges.with_angle(45 .. 90.1).extended_out(0.01).output(lb + 1, dm)
|
||||
a.edges.with_angle(0, 45.1).extended_out(0.01).output(lb + 2, dm)
|
||||
a.edges.without_angle(90.degree).extended_out(0.01).output(lb + 3, dm)
|
||||
a.edges.without_angle(45 .. 90.1).extended_out(0.01).output(lb + 4, dm)
|
||||
a.edges.without_angle(0, 45.1).extended_out(0.01).output(lb + 5, dm)
|
||||
a.with_angle(135).polygons.output(lb + 6, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- with_area #{lb}"
|
||||
|
||||
b.with_area(0.49.um2).output(lb, dm)
|
||||
b.with_area(0 .. 0.5).output(lb + 1, dm)
|
||||
b.with_area(0.5, nil).output(lb + 2, dm)
|
||||
b.without_area(0.49.um2).output(lb + 3, dm)
|
||||
b.without_area(nil, 0.5).output(lb + 4, dm)
|
||||
b.without_area(0.5, nil).output(lb + 5, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- with_perimeter #{lb}"
|
||||
|
||||
b.with_perimeter(2.8.um).output(lb, dm)
|
||||
b.with_perimeter(0 .. 3.um).output(lb + 1, dm)
|
||||
b.with_perimeter(3.um, nil).output(lb + 2, dm)
|
||||
b.without_perimeter(2.8.um).output(lb + 3, dm)
|
||||
b.without_perimeter(nil, 3.um).output(lb + 4, dm)
|
||||
b.without_perimeter(3.um, nil).output(lb + 5, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- with_bbox_height #{lb}"
|
||||
|
||||
b.with_bbox_height(0.7.um).output(lb, dm)
|
||||
b.with_bbox_height(0 .. 0.7.um+1.dbu).output(lb + 1, dm)
|
||||
b.with_bbox_height(0.7.um, nil).output(lb + 2, dm)
|
||||
b.without_bbox_height(0.7.um).output(lb + 3, dm)
|
||||
b.without_bbox_height(nil, 0.7.um+1.dbu).output(lb + 4, dm)
|
||||
b.without_bbox_height(0.7.um, nil).output(lb + 5, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- with_bbox_width #{lb}"
|
||||
|
||||
b.with_bbox_width(0.7.um).output(lb, dm)
|
||||
b.with_bbox_width(0 .. 0.7.um+1.dbu).output(lb + 1, dm)
|
||||
b.with_bbox_width(0.7.um, nil).output(lb + 2, dm)
|
||||
b.without_bbox_width(0.7.um).output(lb + 3, dm)
|
||||
b.without_bbox_width(nil, 0.7.um+1.dbu).output(lb + 4, dm)
|
||||
b.without_bbox_width(0.7.um, nil).output(lb + 5, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- with_bbox_min #{lb}"
|
||||
|
||||
b.with_bbox_min(0.7.um).output(lb, dm)
|
||||
b.with_bbox_min(0 .. 0.7.um+1.dbu).output(lb + 1, dm)
|
||||
b.with_bbox_min(0.7.um, nil).output(lb + 2, dm)
|
||||
b.without_bbox_min(0.7.um).output(lb + 3, dm)
|
||||
b.without_bbox_min(nil, 0.7.um+1.dbu).output(lb + 4, dm)
|
||||
b.without_bbox_min(0.7.um, nil).output(lb + 5, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- with_bbox_max #{lb}"
|
||||
|
||||
b.with_bbox_max(0.7.um).output(lb, dm)
|
||||
b.with_bbox_max(0 .. 0.7.um+1.dbu).output(lb + 1, dm)
|
||||
b.with_bbox_max(0.7.um, nil).output(lb + 2, dm)
|
||||
b.without_bbox_max(0.7.um).output(lb + 3, dm)
|
||||
b.without_bbox_max(nil, 0.7.um+1.dbu).output(lb + 4, dm)
|
||||
b.without_bbox_max(0.7.um, nil).output(lb + 5, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- with_length #{lb}"
|
||||
|
||||
a.edges.with_length(0.6.um).extended_out(0.01).output(lb, dm)
|
||||
a.edges.with_length(0 .. 0.6.um+1.dbu).extended_out(0.01).output(lb + 1, dm)
|
||||
a.edges.with_length(0.6, nil).extended_out(0.01).output(lb + 2, dm)
|
||||
a.edges.without_length(0.6.um).extended_out(0.01).output(lb + 3, dm)
|
||||
a.edges.without_length(0 .. 0.6.um+1.dbu).extended_out(0.01).output(lb + 4, dm)
|
||||
a.edges.without_length(0.6, nil).extended_out(0.01).output(lb + 5, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- ongrid, snap #{lb}"
|
||||
|
||||
a.ongrid(0.2).polygons(1.dbu).output(lb, dm)
|
||||
a.ongrid(0, 0.1).polygons(1.dbu).output(lb + 1, dm)
|
||||
a.snapped(0.2).output(lb + 2, dm)
|
||||
a.snapped(0, 0.1).output(lb + 3, dm)
|
||||
adup = a.dup
|
||||
adup.snap(0.2)
|
||||
adup.xor(a.snapped(0.2)).output(lb + 4, dm)
|
||||
|
||||
lb += 10
|
||||
message "--- odd polygon check #{lb}"
|
||||
|
||||
y.odd_polygons.output(lb, dm)
|
||||
|
||||
end
|
||||
|
||||
if $drc_test_mode == 1
|
||||
|
||||
source($drc_test_source, "TOP")
|
||||
target($drc_test_target, "TOP")
|
||||
run_testsuite(0, 1)
|
||||
|
||||
elsif $drc_test_mode == 2
|
||||
|
||||
target($drc_test_target, "TOPTOP")
|
||||
source($drc_test_source, "TOPTOP")
|
||||
run_testsuite(0, 900)
|
||||
|
||||
elsif $drc_test_mode == 3
|
||||
|
||||
target($drc_test_target, "TOPTOP")
|
||||
source($drc_test_source, "TOPTOP")
|
||||
|
||||
tiles(4.1, 5.2)
|
||||
# must be that big ...
|
||||
tile_borders(1.um, 1.um)
|
||||
threads(4)
|
||||
|
||||
run_testsuite(0, 900, true)
|
||||
|
||||
elsif $drc_test_mode == 4
|
||||
|
||||
target($drc_test_target, "TOPTOP")
|
||||
source($drc_test_source, "TOPTOP")
|
||||
|
||||
tiles(10000.0, 10000.0)
|
||||
tile_borders(0, 0)
|
||||
threads(4)
|
||||
|
||||
run_testsuite(0, 900, true)
|
||||
|
||||
end
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue