Quotes around otherwise ambiguous (underline containing) name
[geos.git] / tests / xmltester / XMLTester.cpp
blobf3192a372f9369211b0476fc0a0d4e75a8292ae0
1 /**********************************************************************
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
6 * Copyright (C) 2005-2006 Refractions Research Inc.
7 * Copyright (C) 2001-2002 Vivid Solutions Inc.
9 * This is free software; you can redistribute and/or modify it under
10 * the terms of the GNU Lesser General Public Licence as published
11 * by the Free Software Foundation.
12 * See the COPYING file for more information.
14 **********************************************************************/
16 #ifdef _MSC_VER
17 # if defined(GEOS_DEBUG_MSVC_USE_VLD) && !defined(GEOS_TEST_USE_STACKWALKER)
18 # include <vld.h>
19 # else
20 //#define _CRTDBG_MAP_ALLOC
21 //#include <stdlib.h>
22 # include <crtdbg.h>
23 # endif
24 #pragma warning(disable : 4127)
25 #endif
27 #include <geos/geom/Point.h>
28 #include <geos/geom/LineString.h>
29 #include <geos/geom/LinearRing.h>
30 #include <geos/geom/Polygon.h>
31 #include <geos/geom/GeometryCollection.h>
32 #include <geos/geom/GeometryFactory.h>
33 #include <geos/geom/IntersectionMatrix.h>
34 #include <geos/geom/PrecisionModel.h>
35 #include <geos/geom/BinaryOp.h>
36 #include <geos/operation/overlay/OverlayOp.h>
37 #include <geos/operation/buffer/BufferBuilder.h>
38 #include <geos/operation/buffer/BufferParameters.h>
39 #include <geos/operation/buffer/BufferOp.h>
40 #include <geos/util.h>
41 #include <geos/util/Interrupt.h>
42 //#include <geos/geomgraph.h>
43 #include <geos/io/WKBReader.h>
44 #include <geos/io/WKBWriter.h>
45 #include <geos/io/WKTReader.h>
46 #include <geos/io/WKTWriter.h>
47 #include <geos/opRelate.h>
48 #include <geos/opPolygonize.h>
49 #include <geos/opLinemerge.h>
50 #include <geos/profiler.h>
51 #include <geos/unload.h>
52 #include <geos/opValid.h>
53 #include "XMLTester.h"
54 #include "BufferResultMatcher.h"
55 #include "SingleSidedBufferResultMatcher.h"
57 #include <cassert>
58 #include <cctype>
59 #include <cstdlib>
60 #include <fstream>
61 #include <functional>
62 #include <iostream>
63 #include <iomanip>
64 #include <sstream>
65 #include <cstring>
66 #include <memory>
67 #include <functional>
68 #include <stdexcept>
69 #include <cmath>
70 #include <stdexcept>
71 #include <algorithm>
73 #include <signal.h>
75 #if defined(_MSC_VER) && defined(GEOS_TEST_USE_STACKWALKER)
76 #include <windows.h>
77 #include "Stackwalker.h"
78 #endif
80 // Geometry methods do use BinaryOp internally
81 #undef USE_BINARYOP
83 using namespace geos;
84 using namespace geos::operation::polygonize;
85 using namespace geos::operation::linemerge;
86 using std::runtime_error;
88 namespace {
90 // a utility function defining a very simple method to indent a line of text
91 const char * getIndent( unsigned int numIndents )
93 static const char * pINDENT = " + ";
94 static const unsigned int LENGTH = strlen( pINDENT );
96 if ( numIndents > LENGTH ) numIndents = LENGTH;
98 return &pINDENT[ LENGTH-numIndents ];
103 void dump_to_stdout( const TiXmlNode * pParent, unsigned int indent = 0 )
105 if ( !pParent ) return;
107 const TiXmlText *pText;
108 int t = pParent->Type();
109 printf( "%s", getIndent( indent));
111 switch ( t )
113 case TiXmlNode::DOCUMENT:
114 printf( "Document" );
115 break;
117 case TiXmlNode::ELEMENT:
118 printf( "Element \"%s\"", pParent->Value() );
119 break;
121 case TiXmlNode::COMMENT:
122 printf( "Comment: \"%s\"", pParent->Value());
123 break;
125 case TiXmlNode::UNKNOWN:
126 printf( "Unknown" );
127 break;
129 case TiXmlNode::TEXT:
130 pText = pParent->ToText();
131 printf( "Text: [%s]", pText->Value() );
132 break;
134 case TiXmlNode::DECLARATION:
135 printf( "Declaration" );
136 break;
137 default:
138 break;
140 printf( "\n" );
142 const TiXmlNode * pChild;
144 for ( pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling())
146 dump_to_stdout( pChild, indent+2 );
152 void
153 tolower(std::string& str)
155 std::transform(str.begin(), str.end(), str.begin(), (int(*)(int))std::tolower);
158 std::string
159 normalize_filename(const std::string& str)
161 std::string newstring;
163 std::string::size_type last_slash = str.find_last_of('/', str.size());
164 if ( last_slash == std::string::npos ) newstring = str;
165 else newstring = str.substr(last_slash+1);
167 for (std::string::iterator i=newstring.begin(), e=newstring.end(); i!=e; ++i)
169 if ( *i == '.' ) *i = '_';
172 tolower(newstring);
174 return newstring;
177 /* Could be an XMLTester class private but oh well.. */
178 static int
179 checkBufferSuccess(geom::Geometry const& gRes, geom::Geometry const& gRealRes, double dist)
181 int success = 1;
185 if ( gRes.getGeometryTypeId() != gRealRes.getGeometryTypeId() )
187 std::cerr << "Expected result is of type "
188 << gRes.getGeometryType()
189 << "; obtained result is of type "
190 << gRealRes.getGeometryType()
191 << std::endl;
192 success=0;
193 break;
196 // Is a buffer always an area ?
197 if ( gRes.getDimension() != 2 )
199 std::cerr << "Don't know how to validate "
200 << "result of buffer operation "
201 << "when expected result is not an "
202 << "areal type."
203 << std::endl;
207 geos::xmltester::BufferResultMatcher matcher;
208 if ( ! matcher.isBufferResultMatch(gRealRes,
209 gRes,
210 dist) )
212 std::cerr << "BufferResultMatcher FAILED" << std::endl;
213 success=0;
214 break;
218 while (0);
220 return success;
223 static int
224 checkSingleSidedBufferSuccess(geom::Geometry& gRes,
225 geom::Geometry& gRealRes, double dist)
227 int success = 1;
231 if ( gRes.getGeometryTypeId() != gRealRes.getGeometryTypeId() )
233 std::cerr << "Expected result is of type "
234 << gRes.getGeometryType()
235 << "; obtained result is of type "
236 << gRealRes.getGeometryType()
237 << std::endl;
238 success=0;
239 break;
242 geos::xmltester::SingleSidedBufferResultMatcher matcher;
243 if ( ! matcher.isBufferResultMatch(gRealRes,
244 gRes,
245 dist) )
247 std::cerr << "SingleSidedBufferResultMatcher FAILED" << std::endl;
248 success=0;
249 break;
253 while (0);
255 return success;
258 XMLTester::XMLTester()
260 gA(0),
261 gB(0),
262 gT(0),
263 pm(0),
264 factory(0),
265 wktreader(0),
266 wktwriter(0),
267 wkbreader(0),
268 wkbwriter(0),
269 test_predicates(0),
270 failed(0),
271 succeeded(0),
272 caseCount(0),
273 testCount(0),
274 testFileCount(0),
275 totalTestCount(0),
276 curr_file(NULL),
277 testValidOutput(false),
278 testValidInput(false),
279 sqlOutput(false),
280 HEXWKB_output(false)
282 setVerbosityLevel(0);
286 XMLTester::setVerbosityLevel(int value)
288 int old_value=verbose;
290 verbose=value;
292 return old_value;
295 /*private*/
296 void
297 XMLTester::printTest(bool success, const std::string& expected_result, const std::string& actual_result, const util::Profile &prof)
299 if ( sqlOutput )
301 std::cout << "INSERT INTO \"" << normalize_filename(*curr_file) << "\" VALUES ("
302 << caseCount << ", "
303 << testCount << ", "
304 << "'" << opSignature << "', "
305 << "'" << curr_case_desc << "', ";
307 std::string geomOut;
309 if ( gA ) {
310 std::cout << "'" << printGeom(gA) << "', ";
311 } else {
312 std::cout << "NULL, ";
315 if ( gB ) {
316 std::cout << "'" << printGeom(gB) << "', ";
317 } else {
318 std::cout << "NULL, ";
321 std::cout << "'" << expected_result << "', "
322 << "'" << actual_result << "', ";
324 if ( success ) std::cout << "'t'";
325 else std::cout << "'f'";
327 std::cout << ");" << std::endl;
330 else
332 std::cout << *curr_file <<":";
333 std::cout << " case" << caseCount << ":";
334 std::cout << " test" << testCount << ": "
335 << opSignature;
336 std::cout << ": " << (success?"ok.":"failed.");
337 std::cout << " (" << std::setprecision(15) << round(prof.getTot()/1000) << " ms)" << std::endl;
339 std::cout << "\tDescription: " << curr_case_desc << std::endl;
342 if ( gA ) {
343 std::cout << "\tGeometry A: ";
344 printGeom(std::cout, gA);
345 std::cout << std::endl;
348 if ( gB ) {
349 std::cout << "\tGeometry B: ";
350 printGeom(std::cout, gB);
351 std::cout << std::endl;
354 std::cout << "\tExpected result: "<<expected_result<<std::endl;
355 std::cout << "\tObtained result: "<<actual_result<<std::endl;
356 std::cout <<std::endl;
360 void
361 XMLTester::run(const std::string &source)
363 curr_file=&source;
365 if ( sqlOutput )
367 std::cout << "CREATE TABLE \"" << normalize_filename(*curr_file) << "\""
368 << "( caseno integer, testno integer, "
369 << " operation varchar, description varchar, "
370 << " a geometry, b geometry, expected geometry, "
371 << " obtained geometry, result bool )"
373 // NOTE: qgis 0.7.4 require oids for proper operations.
374 // The 'WITH OIDS' parameter is supported back to
375 // PostgreSQL 7.2, so if you run an older version
376 // rebuild with the next line commented out.
377 << " WITH OIDS"
379 << ";" << std::endl;
382 ++testFileCount;
384 caseCount=0;
386 if ( ! xml.LoadFile(source.c_str()) )
388 std::stringstream err;
389 err << "Could not load " << source << ": " << xml.ErrorDesc();
390 throw runtime_error(err.str());
393 //dump_to_stdout(&xml);
395 const TiXmlNode* node = xml.FirstChild("run");
397 if ( ! node )
398 throw(runtime_error("Document has no childs"));
400 parseRun(node);
404 void
405 XMLTester::resultSummary(std::ostream &os) const
407 os<<"Files: "<<testFileCount<<std::endl;
408 os<<"Tests: "<<totalTestCount<<std::endl;
409 os<<"Failed: "<<failed<<std::endl;
410 os<<"Succeeded: "<<succeeded<<std::endl;
413 void
414 XMLTester::resetCounters()
416 testFileCount=totalTestCount=failed=succeeded=0;
419 void
420 XMLTester::parseRun(const TiXmlNode* node)
422 using geos::geom::PrecisionModel;
424 assert(node);
426 //dump_to_stdout(node);
428 // Look for precisionModel element
429 const TiXmlElement* el = node->FirstChildElement("precisionModel");
430 if ( el ) parsePrecisionModel(el);
431 else pm.reset(new PrecisionModel());
433 if (verbose > 1)
435 std::cerr << *curr_file <<": run: Precision Model: " << pm->toString() <<std::endl;
438 factory = geom::GeometryFactory::create(pm.get());
439 wktreader.reset(new io::WKTReader(factory.get()));
440 wktwriter.reset(new io::WKTWriter());
441 wkbreader.reset(new io::WKBReader(*factory));
442 wkbwriter.reset(new io::WKBWriter());
444 const TiXmlNode* casenode;
445 for ( casenode = node->FirstChild("case");
446 casenode;
447 casenode = casenode->NextSibling("case") )
449 try {
450 parseCase(casenode);
451 } catch (const std::exception& exc) {
452 std::cerr<<exc.what()<<std::endl;
458 void
459 XMLTester::parsePrecisionModel(const TiXmlElement* el)
461 using geos::geom::PrecisionModel;
463 //dump_to_stdout(el);
465 /* This does not seem to work... */
466 std::string type;
467 const char* typeStr = el->Attribute("type");
468 if ( typeStr ) type = typeStr;
470 const char* scaleStr = el->Attribute("scale");
472 if ( ! scaleStr ) {
473 if ( type == "FLOATING_SINGLE" )
475 pm.reset(new PrecisionModel(PrecisionModel::FLOATING_SINGLE));
477 else
479 pm.reset(new PrecisionModel());
481 } else {
483 char* stopstring;
485 double scale = std::strtod(scaleStr, &stopstring);
486 double offsetX = 0;
487 double offsetY = 2;
489 if ( ! el->QueryDoubleAttribute("offsetx", &offsetX) )
490 {} // std::cerr << "No offsetx" << std::endl;
492 if ( ! el->QueryDoubleAttribute("offsety", &offsetY) )
493 {} // std::cerr << "No offsety" << std::endl;
495 // NOTE: PrecisionModel discards offsets anyway...
496 pm.reset(new PrecisionModel(scale, offsetX, offsetY));
501 bool
502 XMLTester::testValid(const geom::Geometry* g, const std::string& label)
504 operation::valid::IsValidOp ivo(g);
505 bool valid = ivo.isValid();
506 if ( ! valid )
508 operation::valid::TopologyValidationError *err = ivo.getValidationError();
509 std::cerr << *curr_file << ":"
510 << " case" << caseCount << ":"
511 << " test" << testCount << ": "
512 << opSignature << ": "
513 << " invalid geometry (" << label
514 << "): " << err->toString() << std::endl;
516 return valid;
520 * Parse WKT or HEXWKB
522 geom::Geometry *
523 XMLTester::parseGeometry(const std::string &in, const char* label)
525 if ( ( ! wkbreader.get() ) || ( ! wktreader.get() ) )
526 throw(runtime_error("No precision model specified"));
528 std::stringstream is(in, std::ios_base::in);
529 char first_char;
531 // Remove leading spaces
532 while (is.get(first_char) && std::isspace(first_char));
533 is.unget();
535 geom::Geometry* ret;
537 switch (first_char)
539 case '0':
540 case '1':
541 case '2':
542 case '3':
543 case '4':
544 case '5':
545 case '6':
546 case '7':
547 case '8':
548 case '9':
549 case 'A':
550 case 'B':
551 case 'C':
552 case 'D':
553 case 'E':
554 case 'F':
555 ret = wkbreader->readHEX(is);
556 break;
557 default:
558 ret = wktreader->read(in);
559 break;
562 if ( testValidInput ) testValid(ret, std::string(label));
564 //ret->normalize();
566 return ret;
569 std::string
570 XMLTester::trimBlanks(const std::string &in)
572 std::string out;
573 std::string::size_type pos = in.find_first_not_of(" \t\n\r");
574 if (pos!=std::string::npos) out=in.substr(pos);
575 pos = out.find_last_not_of(" \t\n\r");
576 if (pos!=std::string::npos) out=out.substr(0, pos+1);
577 return out;
580 void
581 XMLTester::parseCase(const TiXmlNode* node)
583 assert(node);
585 std::string geomAin;
586 std::string geomBin;
587 std::string thrownException;
589 gA=NULL;
590 gB=NULL;
593 //dump_to_stdout(node);
595 curr_case_desc.clear();
596 const TiXmlNode* txt = node->FirstChild("desc");
597 if ( txt ) {
598 txt = txt->FirstChild();
599 if ( txt ) curr_case_desc = trimBlanks(txt->Value());
602 //std::cerr << "Desc: " << curr_case_desc << std::endl;
605 try {
606 const TiXmlNode *el = node->FirstChild("a");
607 geomAin = el->FirstChild()->Value();
608 geomAin = trimBlanks(geomAin);
609 gA = parseGeometry(geomAin, "Geometry A");
611 if ( 0 != (el = node->FirstChild("b")) )
613 geomBin = el->FirstChild()->Value();
614 geomBin = trimBlanks(geomBin);
615 gB = parseGeometry(geomBin, "Geometry B");
618 catch (const std::exception &e) {
619 thrownException = e.what();
621 catch (...) {
622 thrownException = "Unknown exception";
625 //std::cerr << "A: " << geomAin << std::endl;
626 //std::cerr << "B: " << geomBin << std::endl;
629 if ( thrownException != "" )
631 std::cout << *curr_file <<":";
632 std::cout << " case" << caseCount << ":";
633 std::cout << " skipped ("<<thrownException<<")."<<std::endl;
634 return;
637 ++caseCount;
638 testCount=0;
640 const TiXmlNode* testnode;
641 for ( testnode = node->FirstChild("test");
642 testnode;
643 testnode = testnode->NextSibling("test") )
645 parseTest(testnode);
648 totalTestCount+=testCount;
650 delete gA;
651 delete gB;
654 /*private*/
655 void
656 XMLTester::printGeom(std::ostream& os, const geom::Geometry *g)
658 os << printGeom(g);
661 std::string
662 XMLTester::printGeom(const geom::Geometry *g)
664 if ( HEXWKB_output )
666 std::stringstream s(std::ios_base::binary|std::ios_base::in|std::ios_base::out);
667 wkbwriter->write(*g, s);
668 std::stringstream s2;
669 wkbreader->printHEX(s, s2);
670 return s2.str();
672 else
674 return wktwriter->write(g);
678 void
679 XMLTester::parseTest(const TiXmlNode* node)
681 using namespace operation::overlay;
683 typedef std::auto_ptr< geom::Geometry > GeomAutoPtr;
685 int success=0; // no success by default
686 std::string opName;
687 std::string opArg1;
688 std::string opArg2;
689 std::string opArg3;
690 std::string opArg4;
691 std::string opRes;
693 ++testCount;
695 const TiXmlNode* opnode = node->FirstChild("op");
696 if ( ! opnode ) throw(runtime_error("case has no op"));
698 //dump_to_stdout(opnode);
700 const TiXmlElement* opel = opnode->ToElement();
702 const char* tmp = opel->Attribute("name");
703 if ( tmp ) opName = tmp;
705 tmp = opel->Attribute("arg1");
706 if ( tmp ) opArg1 = tmp;
708 tmp = opel->Attribute("arg2");
709 if ( tmp ) opArg2 = tmp;
711 tmp = opel->Attribute("arg3");
712 if ( tmp ) opArg3 = tmp;
714 tmp = opel->Attribute("arg4");
715 if ( tmp ) opArg4 = tmp;
717 const TiXmlNode* resnode = opnode->FirstChild();
718 if ( ! resnode )
720 std::stringstream tmp;
721 tmp << "op of test " << testCount
722 << " of case " << caseCount
723 << " has no expected result child";
724 throw(runtime_error(tmp.str()));
726 opRes = resnode->Value();
728 // trim blanks
729 opRes=trimBlanks(opRes);
730 opName=trimBlanks(opName);
731 tolower(opName);
733 std::string opSig="";
735 if ( opArg1 != "" ) opSig=opArg1;
736 if ( opArg2 != "" ) {
737 if ( opSig != "" ) opSig += ", ";
738 opSig += opArg2;
740 if ( opArg3 != "" ) {
741 if ( opSig != "" ) opSig += ", ";
742 opSig += opArg3;
744 if ( opArg4 != "" ) {
745 if ( opSig != "" ) opSig += ", ";
746 opSig += opArg4;
749 opSignature = opName + "(" + opSig + ")";
751 std::string actual_result="NONE";
753 // expected_result will be modified by specific tests
754 // if needed (geometry normalization, for example)
755 std::string expected_result=opRes;
757 util::Profile profile("op");
761 if (opName=="relate")
763 std::auto_ptr<geom::IntersectionMatrix> im(gA->relate(gB));
764 assert(im.get());
766 if (im->matches(opArg3)) actual_result="true";
767 else actual_result="false";
769 if (actual_result==opRes) success=1;
771 else if (opName=="relatestring")
773 std::auto_ptr<geom::IntersectionMatrix> im(gA->relate(gB));
774 assert(im.get());
776 actual_result=im->toString();
778 if (actual_result==opRes) success=1;
781 else if (opName=="isvalid")
783 geom::Geometry *gT=gA;
784 if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) {
785 gT=gB;
788 if (gT->isValid()) actual_result="true";
789 else actual_result="false";
791 if (actual_result==opRes) success=1;
795 else if (opName=="intersection")
798 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
799 gRes->normalize();
801 profile.start();
803 #ifndef USE_BINARYOP
804 GeomAutoPtr gRealRes(gA->intersection(gB));
805 #else
806 GeomAutoPtr gRealRes = BinaryOp(gA, gB, overlayOp(OverlayOp::opINTERSECTION));
807 #endif
809 profile.stop();
811 gRealRes->normalize();
813 if (gRes->compareTo(gRealRes.get())==0) success=1;
815 actual_result=printGeom(gRealRes.get());
816 expected_result=printGeom(gRes.get());
818 if ( testValidOutput )
819 success &= int(testValid(gRealRes.get(), "result"));
822 else if (opName=="union")
824 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
826 GeomAutoPtr gRealRes;
827 if ( gB ) {
828 #ifndef USE_BINARYOP
829 gRealRes.reset(gA->Union(gB));
830 #else
831 gRealRes = BinaryOp(gA, gB, overlayOp(OverlayOp::opUNION));
832 #endif
833 } else {
834 gRealRes = gA->Union();
837 if (gRes->equals(gRealRes.get())) success=1;
839 actual_result=printGeom(gRealRes.get());
840 expected_result=printGeom(gRes.get());
842 if ( testValidOutput )
843 success &= int(testValid(gRealRes.get(), "result"));
846 else if (opName=="difference")
849 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
850 gRes->normalize();
852 #ifndef USE_BINARYOP
853 GeomAutoPtr gRealRes(gA->difference(gB));
854 #else
855 GeomAutoPtr gRealRes = BinaryOp(gA, gB, overlayOp(OverlayOp::opDIFFERENCE));
856 #endif
858 gRealRes->normalize();
860 if (gRes->compareTo(gRealRes.get())==0) success=1;
862 actual_result=printGeom(gRealRes.get());
863 expected_result=printGeom(gRes.get());
865 if ( testValidOutput )
866 success &= int(testValid(gRealRes.get(), "result"));
869 else if (opName=="symdifference")
871 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
872 gRes->normalize();
874 #ifndef USE_BINARYOP
875 GeomAutoPtr gRealRes(gA->symDifference(gB));
876 #else
877 GeomAutoPtr gRealRes = BinaryOp(gA, gB, overlayOp(OverlayOp::opSYMDIFFERENCE));
878 #endif
880 gRealRes->normalize();
882 if (gRes->compareTo(gRealRes.get())==0) success=1;
884 actual_result=printGeom(gRealRes.get());
885 expected_result=printGeom(gRes.get());
887 if ( testValidOutput )
888 success &= int(testValid(gRealRes.get(), "result"));
891 else if (opName=="intersects")
893 geom::Geometry *g1 = opArg1 == "B" ? gB : gA;
894 geom::Geometry *g2 = opArg2 == "B" ? gB : gA;
896 if (g1->intersects(g2)) actual_result="true";
897 else actual_result="false";
899 if (actual_result==opRes) success=1;
902 else if (opName=="contains")
904 geom::Geometry *g1 = opArg1 == "B" ? gB : gA;
905 geom::Geometry *g2 = opArg2 == "B" ? gB : gA;
907 if (g1->contains(g2)) actual_result="true";
908 else actual_result="false";
910 if (actual_result==opRes) success=1;
913 else if (opName=="within")
915 geom::Geometry *g1 = opArg1 == "B" ? gB : gA;
916 geom::Geometry *g2 = opArg2 == "B" ? gB : gA;
918 if (g1->within(g2)) actual_result="true";
919 else actual_result="false";
921 if (actual_result==opRes) success=1;
924 else if (opName=="covers")
926 geom::Geometry *g1 = opArg1 == "B" ? gB : gA;
927 geom::Geometry *g2 = opArg2 == "B" ? gB : gA;
929 if (g1->covers(g2)) actual_result="true";
930 else actual_result="false";
932 if (actual_result==opRes) success=1;
935 else if (opName=="coveredby")
937 geom::Geometry *g1 = opArg1 == "B" ? gB : gA;
938 geom::Geometry *g2 = opArg2 == "B" ? gB : gA;
940 if (g1->coveredBy(g2)) actual_result="true";
941 else actual_result="false";
943 if (actual_result==opRes) success=1;
946 else if (opName=="getboundary")
948 geom::Geometry *gT=gA;
949 if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB;
951 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
952 gRes->normalize();
954 GeomAutoPtr gRealRes(gT->getBoundary());
955 gRealRes->normalize();
957 if (gRes->compareTo(gRealRes.get())==0) success=1;
959 actual_result=printGeom(gRealRes.get());
960 expected_result=printGeom(gRes.get());
962 if ( testValidOutput )
963 success &= int(testValid(gRealRes.get(), "result"));
966 else if (opName=="getcentroid")
968 geom::Geometry *gT=gA;
969 if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB;
971 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
972 gRes->normalize();
974 GeomAutoPtr gRealRes(gT->getCentroid());
976 if ( gRealRes.get() ) gRealRes->normalize();
977 else gRealRes.reset(factory->createPoint());
978 gRealRes->normalize();
980 if (gRes->compareTo(gRealRes.get())==0) success=1;
982 actual_result=printGeom(gRealRes.get());
983 expected_result=printGeom(gRes.get());
985 if ( testValidOutput )
986 success &= int(testValid(gRealRes.get(), "result"));
989 else if (opName=="issimple")
991 geom::Geometry *gT=gA;
992 if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB;
994 if (gT->isSimple()) actual_result="true";
995 else actual_result="false";
997 if (actual_result==opRes) success=1;
1001 else if (opName=="convexhull")
1003 geom::Geometry *gT=gA;
1004 if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB;
1006 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
1007 gRes->normalize();
1009 GeomAutoPtr gRealRes(gT->convexHull());
1010 gRealRes->normalize();
1012 if (gRes->compareTo(gRealRes.get())==0) success=1;
1014 actual_result=printGeom(gRealRes.get());
1015 expected_result=printGeom(gRes.get());
1017 if ( testValidOutput )
1018 success &= int(testValid(gRealRes.get(), "result"));
1021 else if (opName=="buffer")
1023 using namespace operation::buffer;
1025 geom::Geometry *gT=gA;
1026 if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB;
1028 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
1029 gRes->normalize();
1031 profile.start();
1033 GeomAutoPtr gRealRes;
1034 double dist = std::atof(opArg2.c_str());
1036 BufferParameters params;
1037 if ( opArg3 != "" ) {
1038 params.setQuadrantSegments(std::atoi(opArg3.c_str()));
1042 BufferOp op(gT, params);
1043 gRealRes.reset(op.getResultGeometry(dist));
1045 profile.stop();
1046 gRealRes->normalize();
1048 // Validate the buffer operation
1049 success = checkBufferSuccess(*gRes, *gRealRes, dist);
1051 actual_result=printGeom(gRealRes.get());
1052 expected_result=printGeom(gRes.get());
1054 if ( testValidOutput )
1055 success &= int(testValid(gRealRes.get(), "result"));
1058 else if (opName=="buffersinglesided")
1060 using namespace operation::buffer;
1062 geom::Geometry *gT=gA;
1063 if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB;
1065 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
1066 gRes->normalize();
1068 profile.start();
1070 GeomAutoPtr gRealRes;
1071 double dist = std::atof(opArg2.c_str());
1073 BufferParameters params ;
1074 params.setJoinStyle( BufferParameters::JOIN_ROUND ) ;
1075 if ( opArg3 != "" ) {
1076 params.setQuadrantSegments( std::atoi(opArg3.c_str()));
1079 bool leftSide = true ;
1080 if ( opArg4 == "right" )
1082 leftSide = false ;
1085 BufferBuilder bufBuilder( params ) ;
1086 gRealRes.reset( bufBuilder.bufferLineSingleSided(
1087 gT, dist, leftSide ) ) ;
1089 profile.stop();
1090 gRealRes->normalize();
1092 // Validate the single sided buffer operation
1093 success = checkSingleSidedBufferSuccess(*gRes,
1094 *gRealRes, dist);
1096 actual_result=printGeom(gRealRes.get());
1097 expected_result=printGeom(gRes.get());
1099 if ( testValidOutput )
1100 success &= int(testValid(gRealRes.get(), "result"));
1103 else if (opName=="buffermitredjoin")
1105 using namespace operation::buffer;
1107 geom::Geometry *gT=gA;
1108 if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB;
1110 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
1111 gRes->normalize();
1113 profile.start();
1115 GeomAutoPtr gRealRes;
1116 double dist = std::atof(opArg2.c_str());
1118 BufferParameters params;
1119 params.setJoinStyle(BufferParameters::JOIN_MITRE);
1121 if ( opArg3 != "" ) {
1122 params.setQuadrantSegments(std::atoi(opArg3.c_str()));
1125 BufferOp op(gT, params);
1126 gRealRes.reset(op.getResultGeometry(dist));
1128 profile.stop();
1129 gRealRes->normalize();
1131 // Validate the buffer operation
1132 success = checkBufferSuccess(*gRes, *gRealRes, dist);
1134 actual_result=printGeom(gRealRes.get());
1135 expected_result=printGeom(gRes.get());
1137 if ( testValidOutput )
1138 success &= int(testValid(gRealRes.get(), "result"));
1142 else if (opName=="getinteriorpoint")
1144 geom::Geometry *gT=gA;
1145 if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB;
1147 GeomAutoPtr gRes(parseGeometry(opRes, "expected"));
1148 gRes->normalize();
1150 GeomAutoPtr gRealRes(gT->getInteriorPoint());
1151 if ( gRealRes.get() ) gRealRes->normalize();
1152 else gRealRes.reset(factory->createPoint());
1154 if (gRes->compareTo(gRealRes.get())==0) success=1;
1156 actual_result=printGeom(gRealRes.get());
1157 expected_result=printGeom(gRes.get());
1159 if ( testValidOutput )
1160 success &= int(testValid(gRealRes.get(), "result"));
1163 else if (opName=="iswithindistance")
1165 double dist=std::atof(opArg3.c_str());
1166 if (gA->isWithinDistance(gB, dist)) {
1167 actual_result="true";
1168 } else {
1169 actual_result="false";
1172 if (actual_result==opRes) success=1;
1176 else if (opName=="polygonize")
1179 GeomAutoPtr gRes(wktreader->read(opRes));
1180 gRes->normalize();
1182 Polygonizer plgnzr;
1183 plgnzr.add(gA);
1186 std::vector<geos::geom::Polygon *>*polys = plgnzr.getPolygons();
1187 std::vector<geom::Geometry *>*newgeoms = new std::vector<geom::Geometry *>;
1188 for (unsigned int i=0; i<polys->size(); i++)
1189 newgeoms->push_back((*polys)[i]);
1190 delete polys;
1192 GeomAutoPtr gRealRes(factory->createGeometryCollection(newgeoms));
1193 gRealRes->normalize();
1196 if (gRes->compareTo(gRealRes.get())==0) success=1;
1198 actual_result=printGeom(gRealRes.get());
1199 expected_result=printGeom(gRes.get());
1201 if ( testValidOutput )
1202 success &= int(testValid(gRealRes.get(), "result"));
1205 else if (opName=="linemerge")
1207 GeomAutoPtr gRes(wktreader->read(opRes));
1208 gRes->normalize();
1210 geom::Geometry *gT=gA;
1212 if ( ( opArg1 == "B" || opArg1 == "b" ) && gB ) gT=gB;
1214 LineMerger merger;
1215 merger.add(gT);
1216 std::auto_ptr< std::vector<geom::LineString *> > lines ( merger.getMergedLineStrings() );
1217 std::vector<geom::Geometry *>*newgeoms = new std::vector<geom::Geometry *>(lines->begin(),
1218 lines->end());
1220 GeomAutoPtr gRealRes(factory->createGeometryCollection(newgeoms));
1221 gRealRes->normalize();
1223 if (gRes->compareTo(gRealRes.get())==0) success=1;
1225 actual_result=printGeom(gRealRes.get());
1226 expected_result=printGeom(gRes.get());
1228 if ( testValidOutput )
1229 success &= int(testValid(gRealRes.get(), "result"));
1232 else if (opName=="areatest")
1234 char* rest;
1235 double toleratedDiff = std::strtod(opRes.c_str(), &rest);
1236 int validOut = 1;
1238 if ( rest == opRes.c_str() )
1240 throw std::runtime_error("malformed testcase: missing tolerated area difference in 'areatest' op");
1243 if ( verbose > 1 )
1245 std::cerr << "Running intersection for areatest" << std::endl;
1247 #ifndef USE_BINARYOP
1248 GeomAutoPtr gI(gA->intersection(gB));
1249 #else
1250 GeomAutoPtr gI = BinaryOp(gA, gB,
1251 overlayOp(OverlayOp::opINTERSECTION));
1252 #endif
1254 if ( testValidOutput )
1256 validOut &= int(testValid(gI.get(), "areatest intersection"));
1259 if ( verbose > 1 )
1261 std::cerr << "Running difference(A,B) for areatest" << std::endl;
1264 #ifndef USE_BINARYOP
1265 GeomAutoPtr gDab(gA->difference(gB));
1266 #else
1267 GeomAutoPtr gDab = BinaryOp(gA, gB,
1268 overlayOp(OverlayOp::opDIFFERENCE));
1269 #endif
1271 if ( testValidOutput )
1273 validOut &= int(testValid(gI.get(), "areatest difference(a,b)"));
1276 if ( verbose > 1 )
1278 std::cerr << "Running difference(B,A) for areatest" << std::endl;
1281 #ifndef USE_BINARYOP
1282 GeomAutoPtr gDba(gB->difference(gA));
1283 #else
1284 GeomAutoPtr gDba = BinaryOp(gB, gA,
1285 overlayOp(OverlayOp::opDIFFERENCE));
1286 #endif
1288 if ( testValidOutput )
1290 validOut &= int(testValid(gI.get(), "areatest difference(b,a)"));
1293 if ( verbose > 1 )
1295 std::cerr << "Running symdifference for areatest" << std::endl;
1298 #ifndef USE_BINARYOP
1299 GeomAutoPtr gSD(gA->symDifference(gB));
1300 #else
1301 GeomAutoPtr gSD = BinaryOp(gA, gB,
1302 overlayOp(OverlayOp::opSYMDIFFERENCE));
1303 #endif
1305 if ( testValidOutput )
1307 validOut &= int(testValid(gI.get(), "areatest symdifference"));
1310 if ( verbose > 1 )
1312 std::cerr << "Running union for areatest" << std::endl;
1315 #ifndef USE_BINARYOP
1316 GeomAutoPtr gU(gA->Union(gB));
1317 #else
1318 GeomAutoPtr gU = BinaryOp(gA, gB,
1319 overlayOp(OverlayOp::opUNION));
1320 #endif
1322 double areaA = gA->getArea();
1323 double areaB = gB->getArea();
1324 double areaI = gI->getArea();
1325 double areaDab = gDab->getArea();
1326 double areaDba = gDba->getArea();
1327 double areaSD = gSD->getArea();
1328 double areaU = gU->getArea();
1330 double maxdiff = 0;
1331 std::string maxdiffop;
1333 // @ : symdifference
1334 // - : difference
1335 // + : union
1336 // ^ : intersection
1338 // A == ( A ^ B ) + ( A - B )
1339 double diff = std::fabs ( areaA - areaI - areaDab );
1340 if ( diff > maxdiff ) {
1341 maxdiffop = "A == ( A ^ B ) + ( A - B )";
1342 maxdiff = diff;
1345 // B == ( A ^ B ) + ( B - A )
1346 diff = std::fabs ( areaB - areaI - areaDba );
1347 if ( diff > maxdiff ) {
1348 maxdiffop = "B == ( A ^ B ) + ( B - A )";
1349 maxdiff = diff;
1352 // ( A @ B ) == ( A - B ) + ( B - A )
1353 diff = std::fabs ( areaDab + areaDba - areaSD );
1354 if ( diff > maxdiff ) {
1355 maxdiffop = "( A @ B ) == ( A - B ) + ( B - A )";
1356 maxdiff = diff;
1359 // ( A u B ) == ( A ^ B ) + ( A @ B )
1360 diff = std::fabs ( areaI + areaSD - areaU );
1361 if ( diff > maxdiff ) {
1362 maxdiffop = "( A u B ) == ( A ^ B ) + ( A @ B )";
1363 maxdiff = diff;
1366 if ( maxdiff <= toleratedDiff )
1368 success = 1 && validOut;
1371 std::stringstream tmp;
1372 tmp << maxdiffop << ": " << maxdiff;
1373 actual_result=tmp.str();
1374 expected_result=opRes;
1377 else if (opName=="distance")
1379 char* rest;
1380 double distE = std::strtod(opRes.c_str(), &rest);
1381 if ( rest == opRes.c_str() )
1383 throw std::runtime_error("malformed testcase: missing expected result in 'distance' op");
1386 geom::Geometry *g1 = opArg1 == "B" ? gB : gA;
1387 geom::Geometry *g2 = opArg2 == "B" ? gB : gA;
1388 double distO = g1->distance(g2);
1389 std::stringstream ss; ss << distO;
1390 actual_result = ss.str();
1392 // TODO: Use a tolerance ?
1393 success = ( distO == distE ) ? 1 : 0;
1396 else
1398 std::cerr << *curr_file << ":";
1399 std::cerr << " case" << caseCount << ":";
1400 std::cerr << " test" << testCount << ": "
1401 << opName << "(" << opSig << ")";
1402 std::cerr << ": skipped (unrecognized)." << std::endl;
1403 return;
1407 catch (const std::exception &e)
1409 std::cerr<<"EXCEPTION on case "<<caseCount
1410 <<" test "<<testCount<<": "<<e.what()
1411 <<std::endl;
1412 actual_result = e.what();
1414 catch (...)
1416 std::cerr << "Unknown EXEPTION on case "
1417 << caseCount
1418 << std::endl;
1419 actual_result = "Unknown exception thrown";
1422 if ( success ) ++succeeded;
1423 else ++failed;
1425 if ((!success && verbose) || verbose > 1)
1427 printTest(!!success, expected_result, actual_result, profile);
1430 if (test_predicates && gB && gA) {
1431 runPredicates(gA, gB);
1436 void
1437 XMLTester::runPredicates(const geom::Geometry *gA, const geom::Geometry *gB)
1439 std::cout << "\t Equals:\tAB=" << (gA->equals(gB)?"T":"F") << ", BA=" << (gB->equals(gA)?"T":"F") << std::endl;
1440 std::cout << "\t Disjoint:\tAB=" << (gA->disjoint(gB)?"T":"F") << ", BA=" << (gB->disjoint(gA)?"T":"F") << std::endl;
1441 std::cout << "\tIntersects:\tAB=" << (gA->intersects(gB)?"T":"F") << ", BA=" << (gB->intersects(gA)?"T":"F") << std::endl;
1442 std::cout << "\t Touches:\tAB=" << (gA->touches(gB)?"T":"F") << ", BA=" << (gB->touches(gA)?"T":"F") << std::endl;
1443 std::cout << "\t Crosses:\tAB=" << (gA->crosses(gB)?"T":"F") << ", BA=" << (gB->crosses(gA)?"T":"F") << std::endl;
1444 std::cout << "\t Within:\tAB=" << (gA->within(gB)?"T":"F") << ", BA=" << (gB->within(gA)?"T":"F") << std::endl;
1445 std::cout << "\t Contains:\tAB=" << (gA->contains(gB)?"T":"F") << ", BA=" << (gB->contains(gA)?"T":"F") << std::endl;
1446 std::cout << "\t Overlaps:\tAB=" << (gA->overlaps(gB)?"T":"F") << ", BA=" << (gB->overlaps(gA)?"T":"F") << std::endl;
1449 XMLTester::~XMLTester()
1454 static void
1455 usage(char *me, int exitcode, std::ostream &os)
1457 os << "Usage: " << me << " [options] <test> [<test> ...]" << std::endl;
1458 os << "Options: " << std::endl;
1459 os << " -v Verbose mode "
1460 << "(multiple -v increment verbosity)" << std::endl
1461 << "--test-valid-output Test output validity" << std::endl
1462 << "--test-valid-input Test input validity" << std::endl
1463 << "--sql-output Produce SQL output" << std::endl
1464 << "--wkb-output Print Geometries as HEXWKB" << std::endl;
1466 std::exit(exitcode);
1469 void
1470 request_interrupt(int sig)
1472 geos::util::Interrupt::request();
1476 main(int argC, char* argV[])
1478 int verbose=0;
1479 bool sql_output=false;
1481 #if defined(_MSC_VER) && defined(GEOS_TEST_USE_STACKWALKER)
1482 InitAllocCheck();
1484 #endif
1486 if ( argC < 2 ) usage(argV[0], 1, std::cerr);
1488 signal(15, request_interrupt);
1490 XMLTester tester;
1491 tester.setVerbosityLevel(verbose);
1493 for (int i=1; i<argC; ++i)
1495 // increment verbosity level
1496 if ( ! std::strcmp(argV[i], "-v" ) )
1498 ++verbose;
1499 tester.setVerbosityLevel(verbose);
1500 continue;
1502 if ( ! std::strcmp(argV[i], "--test-valid-output" ) )
1504 tester.testOutputValidity(true);
1505 continue;
1507 if ( ! std::strcmp(argV[i], "--sql-output" ) )
1509 sql_output = true;
1510 tester.setSQLOutput(sql_output);
1511 continue;
1513 if ( ! std::strcmp(argV[i], "--wkb-output" ) )
1515 sql_output = true;
1516 tester.setHEXWKBOutput(sql_output);
1517 continue;
1519 if ( ! std::strcmp(argV[i], "--test-valid-input" ) )
1521 tester.testInputValidity(true);
1522 continue;
1525 std::string source = argV[i];
1527 try {
1528 tester.run(source);
1529 } catch (const std::exception& exc) {
1530 std::cerr<<exc.what()<<std::endl;
1534 if ( ! sql_output ) tester.resultSummary(std::cout);
1535 else tester.resultSummary(std::cerr);
1537 io::Unload::Release();
1539 return tester.getFailuresCount();
1541 #if defined(_MSC_VER) && defined(GEOS_TEST_USE_STACKWALKER)
1543 DeInitAllocCheck();
1544 #endif
1548 /**********************************************************************
1549 * $Log: XMLTester.cpp,v $
1550 * Revision 1.38 2006/07/13 03:59:10 csavage
1551 * Changes to compile on VC++ - fully qualified polygon name. Should also work on MingW, will test next.
1553 * Revision 1.37 2006/06/19 20:48:35 strk
1554 * parseCase(): make sure to exit the <case> tag before returning
1556 * Revision 1.36 2006/06/14 19:19:10 strk
1557 * Added support for "AreaTest" operations.
1559 * Revision 1.35 2006/06/12 10:39:29 strk
1560 * don't print test file precision model if verbosity level < 2.
1562 * Revision 1.34 2006/06/05 15:36:34 strk
1563 * Given OverlayOp funx code enum a name and renamed values to have a lowercase prefix. Drop all of noding headers from installed header set.
1565 * Revision 1.33 2006/05/19 16:38:22 strk
1566 * * tests/xmltester/XMLTester.cpp: report
1567 * error on load of requested tests.
1569 * Revision 1.32 2006/04/14 14:57:15 strk
1570 * XMLTester binary ops invoked using the new BinaryOp template function.
1572 * Revision 1.31 2006/04/07 13:26:38 strk
1573 * Use of auto_ptr<> to prevent confusing leaks in tester
1575 * Revision 1.30 2006/03/22 16:01:33 strk
1576 * indexBintree.h header split, classes renamed to match JTS
1578 * Revision 1.29 2006/03/17 14:56:39 strk
1579 * Fixed filename normalizer for sql output
1580 **********************************************************************/