Merge branch 'master' into gds2-writer-overflow-checks

This commit is contained in:
Matthias Koefferlein 2024-07-29 19:58:47 +02:00
commit 589a6ce88a
21 changed files with 492 additions and 60 deletions

View File

@ -1,5 +1,4 @@
/*
KLayout Layout Viewer
@ -397,7 +396,7 @@ void LayoutToNetlist::join_nets (const tl::GlobPattern &cell, const std::set<std
m_joined_nets_per_cell.push_back (std::make_pair (cell, gp));
}
#if defined(_DEBUG)
#if defined(HAVE_DEBUG)
static bool check_many_pins (const db::Netlist *netlist)
{
bool ok = true;
@ -430,7 +429,7 @@ void LayoutToNetlist::extract_netlist ()
do_soft_connections ();
// implement the "join_nets" (aka "must connect") feature
#if defined(_DEBUG)
#if defined(HAVE_DEBUG)
// NOTE: the join_nets feature only works for "one pin per net" case
// TODO: either fix that or make sure we do not get multiple pins per net.
// Right now, there no known case that produces multiple pins on a net at

View File

@ -89,6 +89,11 @@ PropertiesRepository::prop_name_id (const tl::Variant &name)
void
PropertiesRepository::change_properties (property_names_id_type id, const properties_set &new_props)
{
// NOTE: change_properties MAY put the property map into a state where there is
// more than one property ID per set. For example, 1 and 5 may be valid property
// ids for the same set. "properties(1)" and "properties(5)" returns the same
// property set "S", while "properties_id(S)" only returns 1.
const properties_set &old_props = properties (id);
std::map <properties_set, properties_id_type>::const_iterator pi = m_properties_ids_by_set.find (old_props);
@ -149,7 +154,10 @@ PropertiesRepository::properties_id (const properties_set &props)
std::map <properties_set, properties_id_type>::const_iterator pi = m_properties_ids_by_set.find (props);
if (pi == m_properties_ids_by_set.end ()) {
properties_id_type id = m_properties_ids_by_set.size ();
properties_id_type id = 0;
if (! m_properties_by_id.empty ()) {
id = (--m_properties_by_id.end ())->first + 1;
}
m_properties_ids_by_set.insert (std::make_pair (props, id));
m_properties_by_id.insert (std::make_pair (id, props));
for (properties_set::const_iterator nv = props.begin (); nv != props.end (); ++nv) {

View File

@ -432,7 +432,7 @@ static const std::set<std::pair<std::string, bool> > &name_map_for_class (const
return cc->second;
}
#if defined(_DEBUG)
#if defined(HAVE_DEBUG)
static std::string type_signature (const gsi::ArgType &t)
{
gsi::ArgType tr (t);
@ -622,7 +622,7 @@ ClassBase::merge_declarations ()
tl_assert (! c->declaration () || c->declaration () == &*c);
}
#if defined(_DEBUG)
#if defined(HAVE_DEBUG)
// do a sanity check
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
@ -634,6 +634,16 @@ ClassBase::merge_declarations ()
method_counts [signature (*m, *s)] += 1;
}
}
// try to obtain the default values to find potential binding issues
for (gsi::MethodBase::argument_iterator a = (*m)->begin_arguments (); a != (*m)->end_arguments (); ++a) {
if (a->spec ()) {
try {
a->spec ()->default_value ();
} catch (tl::Exception &ex) {
tl::warn << "Method " << signature (*m, *(*m)->begin_synonyms ()) << ": error obtaining default value for argument '" << a->spec ()->name () << "': " << ex.msg ();
}
}
}
}
for (std::map<std::string, int>::const_iterator mc = method_counts.begin (); mc != method_counts.end (); ++mc) {
@ -925,4 +935,3 @@ bool has_class (const std::type_info &ti)
}
}

View File

@ -1036,10 +1036,10 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context
// leave it to the consumer to establish the default values (that is faster)
break;
}
const tl::Variant &def_value = a->spec ()->default_value ();
// NOTE: this const_cast means we need to take care that we do not use default values on "out" parameters.
// Otherwise there is a chance we will modify the default value.
gsi::push_arg (arglist, *a, const_cast<tl::Variant &> (def_value), &heap);
// Note: we will use the default value variant for longer, so push it to the heap (#1793)
tl::Variant *def_value = new tl::Variant (a->spec ()->default_value ());
heap.push (def_value);
gsi::push_arg (arglist, *a, *def_value, &heap);
} else {
throw tl::Exception (tl::to_string ("No argument provided (positional or keyword) and no default value available"));
}

View File

@ -359,3 +359,6 @@ DEFINES += \
VERSION = $$KLAYOUT_VERSION
CONFIG(debug, debug|release) {
DEFINES += HAVE_DEBUG
}

View File

@ -461,6 +461,7 @@ GDS2ReaderBase::read_context_info_cell ()
if (valid_hook) {
std::vector <std::string> &strings = m_context_info.insert (std::make_pair (cn, std::vector <std::string> ())).first->second;
std::map <size_t, std::vector<std::string> > strings_ex;
size_t attr = 0;
@ -474,16 +475,60 @@ GDS2ReaderBase::read_context_info_cell ()
attr = size_t (get_ushort ());
} else if (rec_id == sPROPVALUE) {
if (strings.size () <= attr) {
strings.resize (attr + 1, std::string ());
const char *str = get_string ();
// To embed long strings and more than 64k attributes, a separate notation is used:
// "#<n>,<p>:<string>"
// where <n> is a string index and <p> is the part index (zero-based).
// For such properties, the PROPATTR value is ignored. This means however, that the
// attribute numbers may not be unique.
// See issue #1794.
if (str[0] == '#') {
tl::Extractor ex (str + 1);
size_t n = 0, p = 0;
if (ex.try_read (n) && ex.test (",") && ex.try_read (p) && ex.test (":")) {
if (strings.size () <= n) {
strings.resize (n + 1, std::string ());
}
std::vector<std::string> &sv = strings_ex[n];
if (sv.size () <= p) {
sv.resize (p + 1, std::string ());
}
sv[p] = ex.get ();
}
} else {
if (strings.size () <= attr) {
strings.resize (attr + 1, std::string ());
}
strings [attr] = str;
}
strings [attr] = get_string ();
} else {
error (tl::to_string (tr ("ENDEL, PROPATTR or PROPVALUE record expected")));
}
}
}
// combine the multipart strings (#1794)
for (auto es = strings_ex.begin (); es != strings_ex.end (); ++es) {
if (es->first < strings.size ()) {
std::string &s = strings [es->first];
s.clear ();
size_t sz = 0;
for (auto i = es->second.begin (); i != es->second.end (); ++i) {
sz += i->size ();
}
s.reserve (sz);
for (auto i = es->second.begin (); i != es->second.end (); ++i) {
s += *i;
}
}
}
} else {
error (tl::to_string (tr ("Invalid record inside a context info cell")));

View File

@ -155,6 +155,54 @@ static uint16_t safe_convert_to_uint16 (uint64_t value)
return uint16_t (value);
}
void
GDS2WriterBase::write_context_string (size_t n, const std::string &s)
{
// max. size for GDS strings used as payload carrier
size_t chunk_size = 32000;
short max_short = std::numeric_limits<int16_t>::max ();
if (n > size_t (max_short) || s.size () > chunk_size) {
// Split strings and use a separate notation: "#<n>,<+p>:..." for the partial
// strings. n is the string index and p the part index (zero based).
// The property number is not defined in that case. There may be properties with
// the same number. See issue #1794.
size_t nchunks = (s.size () + (chunk_size - 1)) / chunk_size;
while (nchunks > 0) {
--nchunks;
std::string partial;
partial.reserve (chunk_size + 100); // approx.
partial += "#";
partial += tl::to_string (n);
partial += ",";
partial += tl::to_string (nchunks);
partial += ":";
size_t pos = nchunks * chunk_size;
partial += std::string (s, pos, std::min (s.size (), pos + chunk_size) - pos);
write_record_size (6);
write_record (sPROPATTR);
write_short (n <= size_t (max_short) ? short (n) : max_short);
write_string_record (sPROPVALUE, partial);
}
} else {
write_record_size (6);
write_record (sPROPATTR);
write_short (int16_t (n));
write_string_record (sPROPVALUE, s);
}
}
void
GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector<db::cell_index_type> &cells)
{
@ -195,15 +243,9 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data,
// Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings
// will arrive)
for (std::vector <std::string>::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) {
--s;
write_record_size (6);
write_record (sPROPATTR);
write_short (safe_convert_to_uint16 (std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s))); // = user string
write_string_record (sPROPVALUE, *s);
size_t n = std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s);
write_context_string (n, *s);
}
}
@ -234,15 +276,9 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data,
// Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings
// will arrive)
for (std::vector <std::string>::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) {
--s;
write_record_size (6);
write_record (sPROPATTR);
write_short (safe_convert_to_uint16 (std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s))); // = user string
write_string_record (sPROPVALUE, *s);
size_t n = std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s);
write_context_string (n, *s);
}
}

View File

@ -169,6 +169,7 @@ private:
void write_properties (const db::Layout &layout, db::properties_id_type prop_id);
void write_context_cell (db::Layout &layout, const short *time_data, const std::vector<cell_index_type> &cells);
void write_context_string (size_t n, const std::string &s);
};
} // namespace db

View File

@ -1196,7 +1196,7 @@ TEST(121)
}
// Meta info
TEST(130a)
TEST(130)
{
db::Layout layout_org;
@ -1312,6 +1312,113 @@ TEST(130a)
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
}
// Giant meta info (issue #1794)
TEST(131)
{
db::Layout layout_org;
layout_org.add_cell ("U");
db::cell_index_type ci = layout_org.add_cell ("X");
std::vector<tl::Variant> ll1;
std::vector<tl::Variant> ll2;
for (unsigned int i = 0; i < 100000; ++i) {
ll1.push_back (tl::Variant (i));
ll2.push_back ("C" + tl::to_string (i));
}
layout_org.add_meta_info ("a", db::MetaInfo ("", ll1, true));
layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true));
layout_org.add_meta_info (ci, "a", db::MetaInfo ("", ll2, true));
layout_org.add_meta_info (ci, "c", db::MetaInfo ("", -1, true));
std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_131.gds");
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
db::Writer writer (options);
writer.write (layout_org, out);
}
db::Layout layout_read;
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (layout_read.has_meta_info ("x"), false);
EXPECT_EQ (layout_read.has_meta_info ("a"), true);
EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value == ll1, true);
EXPECT_EQ (layout_read.has_meta_info ("b"), true);
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value");
db::cell_index_type ci2 = layout_read.cell_by_name ("X").second;
EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info (ci2, "a").value == ll2, true);
EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1");
}
// Many meta info (issue #1794)
TEST(132)
{
db::Layout layout_org;
layout_org.add_cell ("U");
db::cell_index_type ci = layout_org.add_cell ("X");
for (unsigned int i = 0; i < 100000; ++i) {
layout_org.add_meta_info ("a" + tl::to_string (i), db::MetaInfo ("", i, true));
}
layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true));
for (unsigned int i = 0; i < 100000; ++i) {
layout_org.add_meta_info (ci, "a" + tl::to_string (i * 2), db::MetaInfo ("", i * 2, true));
}
layout_org.add_meta_info (ci, "c", db::MetaInfo ("", -1, true));
std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_132.gds");
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
db::Writer writer (options);
writer.write (layout_org, out);
}
db::Layout layout_read;
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (layout_read.has_meta_info ("x"), false);
EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
for (unsigned int i = 0; i < 10; ++i) {
EXPECT_EQ (layout_read.has_meta_info ("a" + tl::to_string (i)), true);
EXPECT_EQ (layout_read.meta_info ("a" + tl::to_string (i)).value.to_string (), tl::Variant (i).to_string ());
}
EXPECT_EQ (layout_read.has_meta_info ("b"), true);
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value");
db::cell_index_type ci2 = layout_read.cell_by_name ("X").second;
EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
for (unsigned int i = 0; i < 10; ++i) {
EXPECT_EQ (layout_read.has_meta_info (ci2, "a" + tl::to_string (i * 2)), true);
EXPECT_EQ (layout_read.meta_info (ci2, "a" + tl::to_string (i * 2)).value.to_string (), tl::Variant (i * 2).to_string ());
}
EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1");
}
// Limits
static std::string run_test_with_error (double sf, db::Layout &layout)
{

View File

@ -639,6 +639,31 @@ TEST(Bug_1474)
}
}
TEST(Bug_1799)
{
db::Manager m (false);
db::Layout layout (&m);
{
tl::InputStream file (tl::testdata () + "/oasis/issue_1799.oas");
db::OASISReader reader (file);
reader.read (layout);
}
db::properties_id_type pn = layout.properties_repository ().prop_name_id (tl::Variant (1));
db::PropertiesRepository::properties_set ps;
ps.insert (std::make_pair (pn, tl::Variant ("hello, world!")));
auto pid = layout.properties_repository ().properties_id (ps);
auto ps2 = layout.properties_repository ().properties (pid);
EXPECT_EQ (ps2.size (), size_t (1));
EXPECT_EQ (ps2.find (pn) != ps2.end (), true);
if (ps2.find (pn) != ps2.end ()) {
EXPECT_EQ (ps2.find (pn)->second.to_string (), "hello, world!");
}
}
TEST(DuplicateCellname)
{
db::Manager m (false);

View File

@ -764,10 +764,10 @@ push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args
// leave it to the consumer to establish the default values (that is faster)
break;
}
const tl::Variant &def_value = a->spec ()->default_value ();
// NOTE: this const_cast means we need to take care that we do not use default values on "out" parameters.
// Otherwise there is a chance we will modify the default value.
gsi::push_arg (arglist, *a, const_cast<tl::Variant &> (def_value), &heap);
// Note: we will use the default value variant for longer, so push it to the heap (#1793)
tl::Variant *def_value = new tl::Variant (a->spec ()->default_value ());
heap.push (def_value);
gsi::push_arg (arglist, *a, *def_value, &heap);
} else {
throw tl::Exception (tl::to_string (tr ("No argument provided (positional or keyword) and no default value available")));
}

View File

@ -1125,10 +1125,10 @@ push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, VALUE *argv, i
// leave it to the consumer to establish the default values (that is faster)
break;
}
const tl::Variant &def_value = a->spec ()->default_value ();
// NOTE: this const_cast means we need to take care that we do not use default values on "out" parameters.
// Otherwise there is a chance we will modify the default value.
gsi::push_arg (arglist, *a, const_cast<tl::Variant &> (def_value), &heap);
// Note: we will use the default value variant for longer, so push it to the heap (#1793)
tl::Variant *def_value = new tl::Variant (a->spec ()->default_value ());
heap.push (def_value);
gsi::push_arg (arglist, *a, *def_value, &heap);
} else {
throw tl::Exception (tl::to_string (tr ("No argument provided (positional or keyword) and no default value available")));
}

View File

@ -49,18 +49,22 @@ static int default_verbosity ()
return verbosity;
}
static int m_verbosity_level = default_verbosity ();
static int &verbosity_level ()
{
static int level = default_verbosity ();
return level;
}
void
verbosity (int level)
{
m_verbosity_level = level;
verbosity_level () = level;
}
int
verbosity ()
{
return m_verbosity_level;
return verbosity_level ();
}
// ------------------------------------------------

View File

@ -317,15 +317,19 @@ Variant::Variant (const std::vector<char> &ba)
#if defined(HAVE_QT)
Variant::Variant (const QByteArray &qba)
: m_type (t_qbytearray), m_string (0)
: m_type (qba.isNull () ? t_nil : t_qbytearray), m_string (0)
{
m_var.m_qbytearray = new QByteArray (qba);
if (! qba.isNull ()) {
m_var.m_qbytearray = new QByteArray (qba);
}
}
Variant::Variant (const QString &qs)
: m_type (t_qstring), m_string (0)
: m_type (qs.isNull () ? t_nil : t_qstring), m_string (0)
{
m_var.m_qstring = new QString (qs);
if (! qs.isNull ()) {
m_var.m_qstring = new QString (qs);
}
}
Variant::Variant (const QVariant &v)
@ -526,10 +530,14 @@ Variant::Variant (const std::string &s)
}
Variant::Variant (const char *s)
: m_type (t_string)
: m_type (s != 0 ? t_string : t_nil)
{
m_string = new char [strlen (s) + 1];
strcpy (m_string, s);
if (s) {
m_string = new char [strlen (s) + 1];
strcpy (m_string, s);
} else {
m_string = 0;
}
}
Variant::Variant (double d)
@ -676,7 +684,9 @@ Variant::reset ()
Variant &
Variant::operator= (const char *s)
{
if (m_type == t_string && s == m_string) {
if (! s) {
reset ();
} else if (m_type == t_string && s == m_string) {
// we are assigning to ourselves
} else {
char *snew = new char [strlen (s) + 1];
@ -721,7 +731,9 @@ Variant::operator= (const std::vector<char> &s)
Variant &
Variant::operator= (const QByteArray &qs)
{
if (m_type == t_qbytearray && &qs == m_var.m_qbytearray) {
if (qs.isNull ()) {
reset ();
} else if (m_type == t_qbytearray && &qs == m_var.m_qbytearray) {
// we are assigning to ourselves
} else {
QByteArray *snew = new QByteArray (qs);
@ -735,7 +747,9 @@ Variant::operator= (const QByteArray &qs)
Variant &
Variant::operator= (const QString &qs)
{
if (m_type == t_qstring && &qs == m_var.m_qstring) {
if (qs.isNull ()) {
reset ();
} else if (m_type == t_qstring && &qs == m_var.m_qstring) {
// we are assigning to ourselves
} else {
QString *snew = new QString (qs);
@ -1784,9 +1798,9 @@ Variant::to_string () const
if (m_type == t_nil) {
r = "nil";
} else if (m_type == t_double) {
r = tl::to_string (m_var.m_double);
r = tl::to_string (m_var.m_double, 15);
} else if (m_type == t_float) {
r = tl::to_string (m_var.m_float);
r = tl::to_string (m_var.m_float, 7);
} else if (m_type == t_char) {
r = tl::to_string ((int) m_var.m_char);
} else if (m_type == t_schar) {
@ -2421,7 +2435,7 @@ Variant::to_parsable_string () const
} else if (is_ulonglong ()) {
return "#lu" + tl::to_string (to_ulonglong ());
} else if (is_double ()) {
return "##" + tl::to_string (to_double ());
return "##" + tl::to_string (to_double (), 15);
} else if (is_bool ()) {
return m_var.m_bool ? "true" : "false";
} else if (is_nil ()) {

View File

@ -29,6 +29,8 @@
#include <string>
#include <vector>
#include <map>
#include <set>
#include <list>
#include <stdexcept>
#include <typeinfo>
@ -358,7 +360,99 @@ public:
}
/**
* @brief Initialize the Variant with an explicit vector or variants
* @brief Initialize with a const user type (will always create a reference and NOT own the object)
*/
template <class T>
Variant (const T *obj)
: m_type (t_nil), m_string (0)
{
if (obj) {
*this = make_variant_ref (obj);
}
}
/**
* @brief Initialize with a non-const user type (will always create a reference and NOT own the object)
*/
template <class T>
Variant (T *obj)
: m_type (t_nil), m_string (0)
{
if (obj) {
*this = make_variant_ref (obj);
}
}
/**
* @brief Initialize the Variant with a STL vector
*/
template <class T>
Variant (const std::vector<T> &list)
: m_type (t_list), m_string (0)
{
m_var.m_list = new std::vector<tl::Variant> ();
m_var.m_list->reserve (list.size ());
for (auto i = list.begin (); i != list.end (); ++i) {
m_var.m_list->push_back (tl::Variant (*i));
}
}
/**
* @brief Initialize the Variant with a STL list
*/
template <class T>
Variant (const std::list<T> &list)
: m_type (t_list), m_string (0)
{
m_var.m_list = new std::vector<tl::Variant> ();
m_var.m_list->reserve (list.size ());
for (auto i = list.begin (); i != list.end (); ++i) {
m_var.m_list->push_back (tl::Variant (*i));
}
}
/**
* @brief Initialize the Variant with a STL set (not maintaining the set character)
*/
template <class T>
Variant (const std::set<T> &list)
: m_type (t_list), m_string (0)
{
m_var.m_list = new std::vector<tl::Variant> ();
m_var.m_list->reserve (list.size ());
for (auto i = list.begin (); i != list.end (); ++i) {
m_var.m_list->push_back (tl::Variant (*i));
}
}
/**
* @brief Initialize the Variant with a STL pair
*/
template <class A, class B>
Variant (const std::pair<A, B> &pair)
: m_type (t_list), m_string (0)
{
m_var.m_list = new std::vector<tl::Variant> ();
m_var.m_list->reserve (2);
m_var.m_list->push_back (tl::Variant (pair.first));
m_var.m_list->push_back (tl::Variant (pair.second));
}
/**
* @brief Initialize the Variant with a STL map
*/
template <class K, class V>
Variant (const std::map<K, V> &map)
: m_type (t_array), m_string (0)
{
m_var.m_array = new std::map<tl::Variant, tl::Variant> ();
for (auto i = map.begin (); i != map.end (); ++i) {
m_var.m_array->insert (std::make_pair (tl::Variant (i->first), tl::Variant (i->second)));
}
}
/**
* @brief Initialize the Variant with an explicit vector of variants
*/
Variant (const std::vector<tl::Variant> &list)
: m_type (t_list), m_string (0)
@ -366,6 +460,15 @@ public:
m_var.m_list = new std::vector<tl::Variant> (list);
}
/**
* @brief Initialize the Variant with an explicit map of variants
*/
Variant (const std::map<tl::Variant, tl::Variant> &map)
: m_type (t_array), m_string (0)
{
m_var.m_array = new std::map<tl::Variant, tl::Variant> (map);
}
/**
* @brief Initialize the Variant with a list
*/

View File

@ -927,7 +927,7 @@ TEST(8)
bool t;
v = e.parse ("1==2?log('a'):log(2)").execute ();
EXPECT_EQ (v.to_string (), std::string ("0.69314718056"));
EXPECT_EQ (v.to_string (), std::string ("0.693147180559945"));
t = false;
try {
v = e.parse ("1==1?log('a'):log(2)").execute ();

View File

@ -28,10 +28,12 @@
#include "tlTypeTraits.h"
#include "tlUnitTest.h"
#include <cmath>
#include <cstdio>
#include <memory>
#define _USE_MATH_DEFINES // for MSVC
#include <math.h>
struct A
{
std::string a;
@ -1092,8 +1094,84 @@ TEST(6)
EXPECT_EQ (tl::Variant (-0.1 * (1.0 + 1.1e-13)) < tl::Variant (0.1), true);
}
// precision of double serialization
TEST(7a)
{
tl::Variant v (M_PI);
tl::Variant vx;
std::string s (v.to_parsable_string ());
tl::Extractor ex (s.c_str ());
ex.read (vx);
EXPECT_EQ (fabs (M_PI - vx.to_double ()) < 4e-15, true);
}
TEST(7b)
{
tl::Variant v ((float) M_PI);
tl::Variant vx;
std::string s (v.to_parsable_string ());
tl::Extractor ex (s.c_str ());
ex.read (vx);
EXPECT_EQ (fabs (M_PI - vx.to_double ()) < 1e-7, true);
}
// null strings
TEST(8)
{
tl::Variant v ((const char *) 0);
EXPECT_EQ (v.to_parsable_string (), "nil");
v = tl::Variant ("abc");
EXPECT_EQ (v.to_parsable_string (), "'abc'");
v = (const char *) 0;
EXPECT_EQ (v.to_parsable_string (), "nil");
#if defined(HAVE_QT)
v = tl::Variant (QString ());
EXPECT_EQ (v.to_parsable_string (), "nil");
v = tl::Variant (QString::fromUtf8 ("abc"));
EXPECT_EQ (v.to_parsable_string (), "'abc'");
v = QString ();
EXPECT_EQ (v.to_parsable_string (), "nil");
v = tl::Variant (QByteArray ());
EXPECT_EQ (v.to_parsable_string (), "nil");
v = tl::Variant (QByteArray ("abc"));
EXPECT_EQ (v.to_parsable_string (), "'abc'");
v = QByteArray ();
EXPECT_EQ (v.to_parsable_string (), "nil");
#endif
}
// create from STL containers
TEST(9)
{
tl::Variant v;
std::vector<int> vi;
vi.push_back (17);
vi.push_back (1);
std::list<int> li;
li.push_back (42);
li.push_back (-17);
std::set<int> si;
si.insert (31);
si.insert (63);
std::pair<int, int> pi (1, 3);
std::map<int, int> mi;
mi[17] = 42;
mi[-1] = 31;
EXPECT_EQ (tl::Variant (vi).to_parsable_string (), "(#17,#1)");
EXPECT_EQ (tl::Variant (li).to_parsable_string (), "(#42,#-17)");
EXPECT_EQ (tl::Variant (si).to_parsable_string (), "(#31,#63)");
EXPECT_EQ (tl::Variant (pi).to_parsable_string (), "(#1,#3)");
EXPECT_EQ (tl::Variant (mi).to_parsable_string (), "{#-1=>#31,#17=>#42}");
}
// special numeric values
TEST(7)
TEST(10)
{
std::string s;
tl::Extractor ex;

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
testdata/oasis/issue_1799.oas vendored Normal file

Binary file not shown.