Package eoxserver :: Package services :: Module testbase
[hide private]
[frames] | no frames]

Source Code for Module eoxserver.services.testbase

   1  #------------------------------------------------------------------------------- 
   2  # $Id: testbase.py 2395 2013-03-22 18:13:55Z martin.paces $ 
   3  # 
   4  # Project: EOxServer <http://eoxserver.org> 
   5  # Authors: Stephan Krause <stephan.krause@eox.at> 
   6  #          Stephan Meissl <stephan.meissl@eox.at> 
   7  #          Fabian Schindler <fabian.schindler@eox.at> 
   8  #          Martin Paces <martin.paces@eox.at> 
   9  # 
  10  #------------------------------------------------------------------------------- 
  11  # Copyright (C) 2011 EOX IT Services GmbH 
  12  # 
  13  # Permission is hereby granted, free of charge, to any person obtaining a copy 
  14  # of this software and associated documentation files (the "Software"), to deal 
  15  # in the Software without restriction, including without limitation the rights 
  16  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
  17  # copies of the Software, and to permit persons to whom the Software is  
  18  # furnished to do so, subject to the following conditions: 
  19  # 
  20  # The above copyright notice and this permission notice shall be included in all 
  21  # copies of this Software or works derived from this Software. 
  22  # 
  23  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
  24  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  25  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
  26  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  27  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  28  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
  29  # THE SOFTWARE. 
  30  #------------------------------------------------------------------------------- 
  31   
  32  import re  
  33  import os.path 
  34  import logging 
  35  from lxml import etree 
  36  import tempfile 
  37  import mimetypes 
  38  from cStringIO import StringIO 
  39   
  40  from django.test import Client 
  41  from django.conf import settings 
  42   
  43  from eoxserver.core.system import System 
  44  from eoxserver.core.util.xmltools import XMLDecoder 
  45  from eoxserver.core.util.multiparttools import ( 
  46      mpUnpack, getMimeType, getMultipartBoundary 
  47  ) 
  48  from eoxserver.contrib import gdal, osr 
  49  from eoxserver.testing.core import ( 
  50      EOxServerTestCase, BASE_FIXTURES 
  51  ) 
  52  from eoxserver.testing.xcomp import xmlCompareFiles 
  53   
  54   
  55  logger = logging.getLogger(__name__) 
  56   
  57  # THIS IS INTENTIONALLY DOUBLED DUE TO A BUG IN MIMETYPES! 
  58  mimetypes.init() 
  59  mimetypes.init() 
  60   
  61  # precompile regular expression 
  62  RE_MIME_TYPE_XML = re.compile("^text/xml|application/(?:[a-z]+\+)?xml$",re.IGNORECASE) 
  63   
  64  #=============================================================================== 
  65  # Helper functions 
  66  #=============================================================================== 
  67   
68 -def extent_from_ds(ds):
69 gt = ds.GetGeoTransform() 70 size_x = ds.RasterXSize 71 size_y = ds.RasterYSize 72 73 return (gt[0], # minx 74 gt[3] + size_x * gt[5], # miny 75 gt[0] + size_y * gt[1], # maxx 76 gt[3]) # maxy
77
78 -def resolution_from_ds(ds):
79 gt = ds.GetGeoTransform() 80 return (gt[1], abs(gt[5]))
81
82 -def _getMime( s ) :
83 return s.partition(';')[0].strip().lower() 84 #=============================================================================== 85 # Common classes 86 #=============================================================================== 87
88 -class OWSTestCase(EOxServerTestCase):
89 90 """ Main base class for testing the OWS interface 91 of EOxServer. 92 """ 93 94 fixtures = BASE_FIXTURES + ["testing_coverages.json", "testing_asar.json"] 95
96 - def setUp(self):
97 super(OWSTestCase,self).setUp() 98 99 logger.info("Starting Test Case: %s" % self.__class__.__name__) 100 101 rq = self.getRequest() 102 103 if ( len(rq) == 2 ): 104 request, req_type = rq 105 headers = {} 106 else: 107 request, req_type, headers = rq 108 109 client = Client() 110 111 if req_type == "kvp": 112 self.response = client.get('/ows?%s' % request, {}, **headers) 113 114 elif req_type == "xml": 115 self.response = client.post('/ows', request, "text/xml", {}, **headers) 116 117 else: 118 raise Exception("Invalid request type '%s'." % req_type)
119
120 - def isRequestConfigEnabled(self, config_key, default=False):
121 value = System.getConfig().getConfigValue("testing", config_key) 122 if value is None: 123 return default 124 elif value.lower() in ("yes", "y", "true", "on"): 125 return True 126 elif value.lower() in ("no", "n", "false", "off"): 127 return False 128 else: 129 return default
130
131 - def getRequest(self):
132 raise Exception("Not implemented.")
133
134 - def getFileExtension(self, file_type):
135 return "xml"
136
137 - def getResponseFileDir(self):
138 return os.path.join(settings.PROJECT_DIR,"responses")
139
140 - def getDataFileDir(self):
141 return os.path.join(settings.PROJECT_DIR,"data")
142
143 - def getResponseFileName(self, file_type):
144 return "%s.%s" % (self.__class__.__name__, self.getFileExtension(file_type))
145
146 - def getResponseData(self):
147 return self.response.content
148
149 - def getExpectedFileDir(self):
150 return os.path.join(settings.PROJECT_DIR, "expected")
151
152 - def getExpectedFileName(self, file_type):
153 return "%s.%s" % (self.__class__.__name__, self.getFileExtension(file_type))
154
155 - def getXMLData(self):
156 raise Exception("Not implemented.")
157
158 - def _testXMLComparison( self , suffix = "xml" , response = None ):
159 """ 160 Helper function for the basic XML tree comparison to be used by `testXMLComparison`. 161 """ 162 expected_path = os.path.join( self.getExpectedFileDir(), self.getExpectedFileName(suffix) ) 163 response_path = os.path.join( self.getResponseFileDir(), self.getResponseFileName(suffix) ) 164 165 # store the XML response 166 if response is None : response = self.getXMLData() 167 168 # check that the expected XML response exists 169 if not os.path.isfile( expected_path ) : 170 with file(response_path, 'w') as fid : fid.write(response) 171 self.skipTest( "Missing the expected XML response '%s'." % expected_path ) 172 173 # perform the actual comparison 174 try: 175 xmlCompareFiles( expected_path , StringIO(response) ) 176 except Exception as e : 177 with file(response_path, 'w') as fid : fid.write(response) 178 self.fail( "Response returned in '%s' is not equal to expected response in '%s'. REASON: %s " % \ 179 ( response_path , expected_path , str(e) ) )
180 181
182 - def _testBinaryComparison(self, file_type, Data=None):
183 """ 184 Helper function for the `testBinaryComparisonRaster` function. 185 """ 186 expected_path = os.path.join(self.getExpectedFileDir(), self.getExpectedFileName(file_type)) 187 response_path = os.path.join(self.getResponseFileDir(), self.getResponseFileName(file_type)) 188 189 try: 190 f = open(expected_path, 'r') 191 expected = f.read() 192 f.close() 193 except IOError: 194 expected = None 195 196 actual_response = None 197 if Data is None: 198 if file_type in ("raster", "html"): 199 actual_response = self.getResponseData() 200 elif file_type == "xml": 201 actual_response = self.getXMLData() 202 else: 203 self.fail("Unknown file_type '%s'." % file_type) 204 else: 205 actual_response = Data 206 207 if expected != actual_response: 208 if self.getFileExtension("raster") in ("hdf", "nc"): 209 self.skipTest("Skipping binary comparison for HDF or NetCDF file '%s'." % expected_path) 210 f = open(response_path, 'w') 211 f.write(actual_response) 212 f.close() 213 214 if expected is None: 215 self.skipTest("Expected response in '%s' is not present" % expected_path) 216 else: 217 self.fail("Response returned in '%s' is not equal to expected response in '%s'." % ( 218 response_path, expected_path) 219 )
220
221 - def testStatus(self):
222 logger.info("Checking HTTP Status ...") 223 #pylint: disable=E1103 224 self.assertEqual(self.response.status_code, 200)
225 226
227 -class RasterTestCase(OWSTestCase):
228 """ 229 Base class for test cases that expect a raster as response. 230 """ 231
232 - def getFileExtension(self, file_type):
233 return "tif"
234
235 - def testBinaryComparisonRaster(self):
236 if not self.isRequestConfigEnabled("binary_raster_comparison_enabled", True): 237 self.skipTest("Binary raster comparison is explicitly disabled.") 238 self._testBinaryComparison("raster")
239 240
241 -class GDALDatasetTestCase(RasterTestCase):
242 """ 243 Extended RasterTestCases that open the result with GDAL and 244 perform several tests. 245 """ 246
247 - def tearDown(self):
248 super(GDALDatasetTestCase, self).tearDown() 249 try: 250 del self.res_ds 251 del self.exp_ds 252 os.remove(self.tmppath) 253 except AttributeError: 254 pass
255
256 - def _openDatasets(self):
257 _, self.tmppath = tempfile.mkstemp("." + self.getFileExtension("raster")) 258 f = open(self.tmppath, "w") 259 f.write(self.getResponseData()) 260 f.close() 261 gdal.AllRegister() 262 263 exp_path = os.path.join(self.getExpectedFileDir(), self.getExpectedFileName("raster")) 264 265 try: 266 self.res_ds = gdal.Open(self.tmppath, gdal.GA_ReadOnly) 267 except RuntimeError, e: 268 self.fail("Response could not be opened with GDAL. Error was %s" % e) 269 270 try: 271 self.exp_ds = gdal.Open(exp_path, gdal.GA_ReadOnly) 272 except RuntimeError: 273 self.skipTest("Expected response in '%s' is not present" % exp_path)
274
275 -class RectifiedGridCoverageTestCase(GDALDatasetTestCase):
276 - def testSize(self):
277 self._openDatasets() 278 self.assertEqual((self.res_ds.RasterXSize, self.res_ds.RasterYSize), 279 (self.exp_ds.RasterXSize, self.exp_ds.RasterYSize))
280
281 - def testExtent(self):
282 self._openDatasets() 283 EPSILON = 1e-8 284 285 res_extent = extent_from_ds(self.res_ds) 286 exp_extent = extent_from_ds(self.exp_ds) 287 288 self.assert_( 289 max([ 290 abs(res_extent[i] - exp_extent[i]) for i in range(0, 4) 291 ]) < EPSILON 292 )
293
294 - def testResolution(self):
295 self._openDatasets() 296 res_resolution = resolution_from_ds(self.res_ds) 297 exp_resolution = resolution_from_ds(self.exp_ds) 298 self.assertAlmostEqual(res_resolution[0], exp_resolution[0], delta=exp_resolution[0]/10) 299 self.assertAlmostEqual(res_resolution[1], exp_resolution[1], delta=exp_resolution[1]/10)
300
301 - def testBandCount(self):
302 self._openDatasets() 303 self.assertEqual(self.res_ds.RasterCount, self.exp_ds.RasterCount)
304
305 -class ReferenceableGridCoverageTestCase(GDALDatasetTestCase):
306 - def testSize(self):
307 self._openDatasets() 308 self.assertEqual((self.res_ds.RasterXSize, self.res_ds.RasterYSize), 309 (self.exp_ds.RasterXSize, self.exp_ds.RasterYSize))
310
311 - def testBandCount(self):
312 self._openDatasets() 313 self.assertEqual(self.res_ds.RasterCount, self.exp_ds.RasterCount)
314
315 - def testGCPs(self):
316 self._openDatasets() 317 self.assertEqual(self.res_ds.GetGCPCount(), self.exp_ds.GetGCPCount())
318
319 - def testGCPProjection(self):
320 self._openDatasets() 321 322 res_proj = self.res_ds.GetGCPProjection() 323 if not res_proj: 324 self.fail("Response Dataset has no GCP Projection defined") 325 res_srs = osr.SpatialReference(res_proj) 326 327 exp_proj = self.exp_ds.GetGCPProjection() 328 if not exp_proj: 329 self.fail("Expected Dataset has no GCP Projection defined") 330 exp_srs = osr.SpatialReference(exp_proj) 331 332 self.assert_(res_srs.IsSame(exp_srs))
333
334 -class XMLTestCase(OWSTestCase):
335 """ 336 Base class for test cases that expects XML output, which is parsed 337 and validated against a schema definition. 338 """ 339
340 - def getXMLData(self):
341 return self.response.content
342
343 - def testValidate(self, XMLData=None):
344 logger.info("Validating XML ...") 345 346 if XMLData is None: 347 doc = etree.XML(self.getXMLData()) 348 else: 349 doc = etree.XML(XMLData) 350 schema_locations = doc.get("{http://www.w3.org/2001/XMLSchema-instance}schemaLocation") 351 locations = schema_locations.split() 352 353 # get schema locations 354 schema_def = etree.Element("schema", attrib={ 355 "elementFormDefault": "qualified", 356 "version": "1.0.0", 357 }, nsmap={ 358 None: "http://www.w3.org/2001/XMLSchema" 359 } 360 ) 361 362 for ns, location in zip(locations[::2], locations[1::2]): 363 if location == "../owsCoverages.xsd": 364 location = "http://schemas.opengis.net/wcs/1.1/wcsAll.xsd" 365 etree.SubElement(schema_def, "import", attrib={ 366 "namespace": ns, 367 "schemaLocation": location 368 } 369 ) 370 371 # TODO: ugly workaround. But otherwise, the doc is not recognized as schema 372 schema = etree.XMLSchema(etree.XML(etree.tostring(schema_def))) 373 374 try: 375 schema.assertValid(doc) 376 except etree.Error as e: 377 self.fail(str(e))
378
379 - def testXMLComparison(self):
380 self._testXMLComparison()
381
382 -class SchematronTestMixIn(object): # requires to be mixed in with XMLTestCase
383 """ 384 Mixin class for XML test cases that uses XML schematrons for validation. 385 Use the `schematron_locations` 386 """ 387 schematron_locations = () 388
389 - def testSchematron(self):
390 errors = [] 391 doc = etree.XML(self.getXMLData()) 392 393 schematron_def = etree.Element("schema", attrib={ 394 "queryBinding": "xslt2", 395 }, nsmap={ 396 None: "http://purl.oclc.org/dsdl/schematron" 397 } 398 ) 399 etree.SubElement(schematron_def, "pattern") 400 401 # TODO: Check if this is even possible: 402 # for ns, location in zip(self.schematron_locations[::2], self.schematron_locations[1::2]): 403 # etree.SubElement(schematron_def, "import", attrib={ 404 # "namespace": ns, 405 # "schemaLocation": location 406 # } 407 # ) 408 409 # TODO: ugly workaround. But otherwise, the doc is not recognized as schema 410 schematron = etree.Schematron(etree.XML(etree.tostring(schematron_def))) 411 412 try: 413 schematron.assertValid(doc) 414 except etree.DocumentInvalid, e: 415 errors.append(str(e)) 416 except etree.SchematronValidateError: 417 self.skipTest("Schematron Testing is not enabled.") 418 419 if len(errors): 420 self.fail(str(errors))
421 422
423 -class ExceptionTestCase(XMLTestCase):
424 """ 425 Exception test cases expect the request to fail and examine the 426 exception response. 427 """ 428
429 - def getExpectedHTTPStatus(self):
430 return 400
431
432 - def getExpectedExceptionCode(self):
433 return ""
434
435 - def getExceptionCodeLocation(self):
436 return "/ows:Exception/@exceptionCode"
437
438 - def testStatus(self):
439 logger.info("Checking HTTP Status ...") 440 #pylint: disable=E1103 441 self.assertEqual(self.response.status_code, self.getExpectedHTTPStatus())
442
443 - def testExceptionCode(self):
444 logger.info("Checking OWS Exception Code ...") 445 decoder = XMLDecoder(self.getXMLData(), { 446 "exceptionCode": {"xml_location": self.getExceptionCodeLocation(), "xml_type": "string"} 447 }) 448 self.assertEqual(decoder.getValue("exceptionCode"), self.getExpectedExceptionCode())
449
450 -class HTMLTestCase(OWSTestCase):
451 """ 452 HTML test cases expect to receive HTML text. 453 """ 454
455 - def getFileExtension(self, file_type):
456 return "html"
457
458 - def testBinaryComparisonHTML(self):
459 self._testBinaryComparison("html")
460
461 -class MultipartTestCase(XMLTestCase):
462 """ 463 Multipart tests combine XML and raster tests and split the response 464 into a xml and a raster part which are examined separately. 465 """ 466
467 - def setUp(self):
468 self.xmlData = None 469 self.imageData = None 470 self.isSetUp = False 471 super(MultipartTestCase, self).setUp() 472 473 self._setUpMultiparts()
474 475
476 - def _mangleXML( self , cbuffer ) :
477 """ remove variable parts of selected XML elements text and attributes """ 478 479 #define XML names to be used 480 481 N0 = "{http://www.opengis.net/gml/3.2}rangeSet/" \ 482 "{http://www.opengis.net/gml/3.2}File/" \ 483 "{http://www.opengis.net/gml/3.2}rangeParameters" 484 485 N1 = "{http://www.opengis.net/gml/3.2}rangeSet/" \ 486 "{http://www.opengis.net/gml/3.2}File/" \ 487 "{http://www.opengis.net/gml/3.2}fileReference" 488 489 N2 = "{http://www.opengis.net/wcs/1.1}Coverage/" \ 490 "{http://www.opengis.net/ows/1.1}Reference" 491 492 HREF = "{http://www.w3.org/1999/xlink}href" 493 494 # define handy closures 495 496 def cropFileName( name ) : 497 base , _ , ext = name.rpartition(".") 498 base , _ , _ = base.rpartition("_") 499 return "%s.%s"%(base,ext)
500 501 def changeHref( e ) : 502 if e is not None : e.set( HREF , cropFileName( e.get( HREF ) ) ) 503 504 def changeText( e ) : 505 if e is not None: e.text = cropFileName( e.text ) 506 507 # parse XML Note: etree.parse respects the encoding reported in XML delaration! 508 xml = etree.parse( StringIO( cbuffer ) ) 509 510 # mangle XML content to get rid of variable content 511 512 # WCS 2.0.x - rangeSet/File 513 changeHref( xml.find( N0 ) ) 514 changeText( xml.find( N1 ) ) 515 # WCS 1.1.x - Coverage/Reference 516 changeHref( xml.find( N2 ) ) 517 518 # output xml - force UTF-8 encoding including the XML delaration 519 return etree.tostring( xml , encoding="UTF-8" , xml_declaration=True ) 520
521 - def _unpackMultipartContent( self , response ) :
522 523 content = response.content 524 content_type = response['Content-Type'] 525 526 # check the content type 527 if getMimeType( content_type ) \ 528 not in ( "multipart/mixed" , "multipart/related" ) : 529 raise Exception , "Received content is neither mixed nor related multipart! Content-Type: %s" % content_type 530 531 # extract multipart boundary 532 boundary = getMultipartBoundary( content_type ) 533 534 # unpack the multipart content 535 for header,offset,size in mpUnpack(content,boundary) : 536 match = RE_MIME_TYPE_XML.match( _getMime(header['content-type']) ) 537 if match is not None : 538 # store XML response 539 self.xmlData = content[offset:(offset+size)] 540 else : 541 # store coverage data 542 self.imageData = content[offset:(offset+size)] 543 544
545 - def _setUpMultiparts(self):
546 if self.isSetUp: return 547 548 self._unpackMultipartContent( self.response ) 549 550 # mangle XML data 551 if self.xmlData : self.xmlData = self._mangleXML( self.xmlData ) 552 553 self.isSetUp = True
554 555
556 - def getFileExtension(self, part=None):
557 if part == "xml": 558 return "xml" 559 elif part == "raster": 560 return "tif" 561 elif part == "TransactionDescribeCoverage": 562 return "TransactionDescribeCoverage.xml" 563 elif part == "TransactionDeleteCoverage": 564 return "TransactionDeleteCoverage.xml" 565 elif part == "TransactionDescribeCoverageDeleted": 566 return "TransactionDescribeCoverageDeleted.xml" 567 else: 568 return "dat"
569
570 - def getXMLData(self):
571 self._setUpMultiparts() 572 return self.xmlData
573
574 - def getResponseData(self):
575 self._setUpMultiparts() 576 return self.imageData
577
578 -class RectifiedGridCoverageMultipartTestCase( 579 MultipartTestCase, 580 RectifiedGridCoverageTestCase 581 ):
582 pass
583
584 -class ReferenceableGridCoverageMultipartTestCase( 585 MultipartTestCase, 586 ReferenceableGridCoverageTestCase 587 ):
588 pass
589 590 #=============================================================================== 591 # WCS-T 592 #=============================================================================== 593
594 -class WCSTransactionTestCase(XMLTestCase):
595 """ 596 Base class for WCS Transactional test cases. 597 """ 598 ADDmetaFile = None 599 ADDtiffFile = None 600 isAsync = False 601 ID = None 602
603 - def setUp(self):
604 super(WCSTransactionTestCase, self).setUp() 605 logger.debug("WCSTransactionTestCase for ID: %s" % self.ID) 606 607 if self.isAsync: 608 from eoxserver.resources.processes.tracker import ( 609 dequeueTask, TaskStatus, startTask, stopTaskSuccessIfNotFinished 610 ) 611 612 # get a pending task from the queue 613 taskId = dequeueTask(1)[0] 614 615 # create instance of TaskStatus class 616 pStatus = TaskStatus( taskId ) 617 try: 618 # get task parameters and change status to STARTED 619 requestType , requestID , requestHandler , inputs = startTask( taskId ) 620 # load the handler 621 module , _ , funct = requestHandler.rpartition(".") 622 handler = getattr( __import__(module,fromlist=[funct]) , funct ) 623 # execute handler 624 handler( pStatus , inputs ) 625 # if no terminating status has been set do it right now 626 stopTaskSuccessIfNotFinished( taskId ) 627 except Exception as e : 628 pStatus.setFailure( unicode(e) ) 629 630 # Add DescribeCoverage request/response 631 request = "service=WCS&version=2.0.0&request=DescribeCoverage&coverageid=%s" % str( self.ID ) 632 self.responseDescribeCoverage = self.client.get('/ows?%s' % request) 633 634 # Add GetCoverage request/response 635 request = "service=WCS&version=2.0.0&request=GetCoverage&format=image/tiff&mediatype=multipart/mixed&coverageid=%s" % str( self.ID ) 636 self.responseGetCoverage = self.client.get('/ows?%s' % request) 637 638 # Add delete coverage request/response 639 requestBegin = """<wcst:Transaction service="WCS" version="1.1" 640 xmlns:wcst="http://www.opengis.net/wcs/1.1/wcst" 641 xmlns:ows="http://www.opengis.net/ows/1.1" 642 xmlns:xlink="http://www.w3.org/1999/xlink" 643 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 644 xsi:schemaLocation="http://www.opengis.net/wcs/1.1/wcst http://schemas.opengis.net/wcst/1.1/wcstTransaction.xsd"> 645 <wcst:InputCoverages> 646 <wcst:Coverage> 647 <ows:Identifier>""" 648 requestEnd = """</ows:Identifier> 649 <wcst:Action codeSpace=\"http://schemas.opengis.net/wcs/1.1.0/actions.xml\"> 650 Delete 651 </wcst:Action>" 652 </wcst:Coverage> 653 </wcst:InputCoverages> 654 </wcst:Transaction>""" 655 request = requestBegin + self.ID + requestEnd 656 self.responseDeleteCoverage = self.client.post('/ows', request, "text/xml") 657 658 # Add DescribeCoverage request/response after delete 659 request = "service=WCS&version=2.0.0&request=DescribeCoverage&coverageid=%s" % str( self.ID ) 660 self.responseDescribeCoverageDeleted = self.client.get('/ows?%s' % request)
661
662 - def getRequest(self):
663 requestBegin = """<wcst:Transaction service="WCS" version="1.1" 664 xmlns:wcst="http://www.opengis.net/wcs/1.1/wcst" 665 xmlns:ows="http://www.opengis.net/ows/1.1" 666 xmlns:xlink="http://www.w3.org/1999/xlink" 667 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 668 xsi:schemaLocation="http://www.opengis.net/wcs/1.1/wcst http://schemas.opengis.net/wcst/1.1/wcstTransaction.xsd"> 669 <wcst:InputCoverages> 670 <wcst:Coverage> 671 <ows:Identifier>""" 672 requestMid1 = """</ows:Identifier> 673 <ows:Reference xlink:href="file:///""" 674 requestMid2 = """" xlink:role="urn:ogc:def:role:WCS:1.1:Pixels"/>""" 675 requestMid3 = """<ows:Metadata xlink:href="file:///""" 676 requestMid4 = """" xlink:role="http://www.opengis.net/eop/2.0/EarthObservation"/>""" 677 requestMid5 = """<wcst:Action codeSpace="http://schemas.opengis.net/wcs/1.1.0/actions.xml">Add</wcst:Action> 678 </wcst:Coverage> 679 </wcst:InputCoverages>""" 680 requestAsync = """<wcst:ResponseHandler>http://NOTUSED</wcst:ResponseHandler>""" 681 requestEnd = """</wcst:Transaction>""" 682 683 params = requestBegin + self.ID + requestMid1 + self.getDataFullPath(self.ADDtiffFile) + requestMid2 684 if self.ADDmetaFile is not None: 685 params += requestMid3 + self.getDataFullPath(self.ADDmetaFile) + requestMid4 686 params += requestMid5 687 if self.isAsync: 688 params += requestAsync 689 params += requestEnd 690 return (params, "xml")
691
692 - def getDataFullPath(self , path_to):
693 return os.path.abspath( os.path.join( self.getDataFileDir() , path_to) )
694
695 - def testXMLComparison(self):
696 # the TimeStamp and RequestId elements are set during ingestion and 697 # thus have to be explicitly unified 698 tree = etree.fromstring(self.getXMLData()) 699 for node in tree.findall("{http://www.opengis.net/wcs/1.1/wcst}RequestId"): 700 node.text = "identifier" 701 for node in tree.findall("{http://www.opengis.net/wcs/1.1/wcst}TimeStamp"): 702 node.text = "2011-01-01T00:00:00Z" 703 self.response.content = etree.tostring(tree, encoding="ISO-8859-1") 704 super(WCSTransactionTestCase, self).testXMLComparison()
705
707 """ 708 Tests that the <ows:Identifier> in the XML request and response is the 709 same 710 """ 711 logger.debug("IDCompare testResponseIdComparison for ID: %s" % self.ID) 712 self._testResponseIdComparison( self.ID , self.getXMLData() )
713
714 - def testStatusDescribeCoverage(self):
715 """ 716 Tests that the inserted coverage is available in a DescribeCoverage 717 request 718 """ 719 #pylint: disable=E1103 720 self.assertEqual(self.responseDescribeCoverage.status_code, 200)
721
723 self.testValidate(self.responseDescribeCoverage.content)
724
726 #self._testBinaryComparison("TransactionDescribeCoverage", self.responseDescribeCoverage.content) 727 self._testXMLComparison( "TransactionDescribeCoverage" , self.responseDescribeCoverage.content )
728
729 - def testStatusGetCoverage(self):
730 """ 731 Validate the inserted coverage via a GetCoverage request 732 """ 733 #pylint: disable=E1103 734 self.assertEqual(self.responseGetCoverage.status_code, 200)
735
736 - def testStatusDeleteCoverage(self):
737 """ 738 Test to delete the previously inserted coaverage 739 """ 740 #pylint: disable=E1103 741 self.assertEqual(self.responseDeleteCoverage.status_code, 200)
742
743 - def testValidateDeleteCoverage(self):
744 self.testValidate(self.responseDeleteCoverage.content)
745
747 tree = etree.fromstring(self.responseDeleteCoverage.content) 748 for node in tree.findall("{http://www.opengis.net/wcs/1.1/wcst}RequestId"): 749 node.text = "identifier" 750 #self._testBinaryComparison("TransactionDeleteCoverage", etree.tostring(tree, encoding="ISO-8859-1")) 751 self._testXMLComparison( "TransactionDeleteCoverage" , etree.tostring(tree, encoding="ISO-8859-1"))
752
754 """ 755 Tests that the <ows:Identifier> in the XML request and response is the 756 same 757 """ 758 logger.debug("IDCompare testResponseIdComparison for ID: %s" % self.ID) 759 self._testResponseIdComparison( self.ID , self.responseDeleteCoverage.content )
760
762 """ 763 Tests that the deletec coverage is not longer available in a 764 DescribeCoverage request 765 """ 766 #pylint: disable=E1103 767 self.assertEqual(self.responseDescribeCoverageDeleted.status_code, 404)
768
770 self.testValidate(self.responseDescribeCoverageDeleted.content)
771
773 #self._testBinaryComparison("TransactionDescribeCoverageDeleted", self.responseDescribeCoverageDeleted.content) 774 self._testXMLComparison( "TransactionDescribeCoverageDeleted" , self.responseDescribeCoverageDeleted.content )
775
776 - def _testResponseIdComparison(self , id , rcontent ):
777 """ 778 Tests that the <ows:Identifier> in the XML request and response is the 779 same 780 """ 781 logger.debug("_testResponseIdComparison for ID: %s" % id) 782 tree = etree.fromstring( rcontent ) 783 for node in tree.findall("{http://www.opengis.net/ows/1.1}Identifier"): 784 self.assertEqual( node.text, id )
785
786 -class WCSTransactionRectifiedGridCoverageTestCase( 787 RectifiedGridCoverageMultipartTestCase, 788 WCSTransactionTestCase 789 ):
790 """ 791 WCS-T test cases for RectifiedGridCoverages 792 """ 793 # Overwrite _setUpMultiparts() to return the GetCoverage response to be used 794 # in MultipartTestCase tests
795 - def _setUpMultiparts(self):
796 if self.isSetUp: return 797 798 self._unpackMultipartContent( self.responseGetCoverage ) 799 800 self.isSetUp = True
801
802 - def getXMLData(self):
803 return self.response.content
804
805 -class WCSTransactionReferenceableGridCoverageTestCase( 806 ReferenceableGridCoverageMultipartTestCase, 807 WCSTransactionTestCase 808 ):
809 """ 810 WCS-T test cases for ReferenceableGridCoverages 811 """ 812 # Overwrite _setUpMultiparts() to return the GetCoverage response to be used 813 # in MultipartTestCase tests
814 - def _setUpMultiparts(self):
815 if self.isSetUp: return 816 817 self._unpackMultipartContent( self.responseGetCoverage ) 818 819 self.isSetUp = True
820
821 - def getXMLData(self):
822 return self.response.content
823 824 #=============================================================================== 825 # WCS 2.0 826 #=============================================================================== 827
828 -class WCS20DescribeEOCoverageSetSubsettingTestCase(XMLTestCase):
829 - def getExpectedCoverageIds(self):
830 return []
831
832 - def testCoverageIds(self):
833 logger.info("Checking Coverage Ids ...") 834 decoder = XMLDecoder(self.getXMLData(), { 835 "coverageids": {"xml_location": "/wcs:CoverageDescriptions/wcs:CoverageDescription/wcs:CoverageId", "xml_type": "string[]"} 836 }) 837 838 result_coverage_ids = decoder.getValue("coverageids") 839 expected_coverage_ids = self.getExpectedCoverageIds() 840 self.assertItemsEqual(result_coverage_ids, expected_coverage_ids) 841 842 # assert that every coverage ID is unique in the response 843 for coverage_id in result_coverage_ids: 844 self.assertTrue(result_coverage_ids.count(coverage_id) == 1, "CoverageID %s is not unique." % coverage_id)
845
846 -class WCS20DescribeEOCoverageSetPagingTestCase(XMLTestCase):
847 - def getExpectedCoverageCount(self):
848 return 0
849
850 - def testCoverageCount(self):
851 decoder = XMLDecoder(self.getXMLData(), { 852 "coverageids": {"xml_location": "/wcs:CoverageDescriptions/wcs:CoverageDescription/wcs:CoverageId", "xml_type": "string[]"} 853 }) 854 coverage_ids = decoder.getValue("coverageids") 855 self.assertEqual(len(coverage_ids), self.getExpectedCoverageCount())
856
857 -class WCS20DescribeEOCoverageSetSectionsTestCase(XMLTestCase):
858 - def getExpectedSections(self):
859 return []
860
861 - def testSections(self):
862 decoder = XMLDecoder(self.getXMLData(), { 863 "sections": {"xml_location": "/*", "xml_type": "tagName[]"} 864 }) 865 sections = decoder.getValue("sections") 866 self.assertItemsEqual(sections, self.getExpectedSections())
867
868 -class WCS20GetCoverageMultipartTestCase(MultipartTestCase):
869 - def testXMLComparison(self):
870 # The timePosition tag depends on the actual time the request was 871 # answered. It has to be explicitly unified. 872 tree = etree.fromstring(self.getXMLData()) 873 for node in tree.findall("{http://www.opengis.net/gmlcov/1.0}metadata/" \ 874 "{http://www.opengis.net/gmlcov/1.0}Extension/" \ 875 "{http://www.opengis.net/wcseo/1.0}EOMetadata/" \ 876 "{http://www.opengis.net/wcseo/1.0}lineage/" \ 877 "{http://www.opengis.net/gml/3.2}timePosition"): 878 node.text = "2011-01-01T00:00:00Z" 879 self.xmlData = etree.tostring(tree, encoding="ISO-8859-1") 880 881 super(WCS20GetCoverageMultipartTestCase, self).testXMLComparison()
882
883 -class WCS20GetCoverageRectifiedGridCoverageMultipartTestCase( 884 WCS20GetCoverageMultipartTestCase, 885 RectifiedGridCoverageTestCase 886 ):
887 pass
888
889 -class WCS20GetCoverageReferenceableGridCoverageMultipartTestCase( 890 WCS20GetCoverageMultipartTestCase, 891 ReferenceableGridCoverageTestCase 892 ):
893 pass
894
895 -class RasdamanTestCaseMixIn(object):
896 fixtures = BASE_FIXTURES + ["testing_rasdaman_coverages.json"] 897
898 - def setUp(self):
899 # TODO check if connection to DB server is possible 900 # TODO check if datasets are configured within the DB 901 902 gdal.AllRegister() 903 if gdal.GetDriverByName("RASDAMAN") is None: 904 self.skipTest("Rasdaman driver is not enabled.") 905 906 if not self.isRequestConfigEnabled("rasdaman_enabled"): 907 self.skipTest("Rasdaman tests are not enabled. Use the " 908 "configuration option 'rasdaman_enabled' to allow " 909 "rasdaman tests.") 910 911 super(RasdamanTestCaseMixIn, self).setUp()
912 913 #=============================================================================== 914 # WMS test classes 915 #=============================================================================== 916
917 -class WMS11GetMapTestCase(RasterTestCase):
918 layers = [] 919 styles = [] 920 crs = "epsg:4326" 921 bbox = (0, 0, 1, 1) 922 width = 100 923 height = 100 924 frmt = "image/jpeg" 925 time = None 926 dim_band = None 927 928 swap_axes = True 929 930 httpHeaders = None 931
932 - def getFileExtension(self, part=None):
933 return mimetypes.guess_extension(self.frmt, False)[1:]
934
935 - def getRequest(self):
936 params = "service=WMS&request=GetMap&version=1.1.1&" \ 937 "layers=%s&styles=%s&srs=%s&bbox=%s&" \ 938 "width=%d&height=%d&format=%s" % ( 939 ",".join(self.layers), ",".join(self.styles), self.crs, 940 ",".join(map(str, self.bbox)), 941 self.width, self.height, self.frmt 942 ) 943 944 if self.time: 945 params += "&time=%s" % self.time 946 947 if self.dim_band: 948 params += "&dim_band=%s" % self.dim_band 949 950 if self.httpHeaders is None: 951 return (params, "kvp") 952 else: 953 return (params, "kvp", self.httpHeaders)
954
955 -class WMS13GetMapTestCase(RasterTestCase):
956 layers = [] 957 styles = [] 958 crs = "epsg:4326" 959 bbox = (0, 0, 1, 1) 960 width = 100 961 height = 100 962 frmt = "image/jpeg" 963 time = None 964 dim_band = None 965 966 swap_axes = True 967 968 httpHeaders = None 969
970 - def getFileExtension(self, part=None):
971 return mimetypes.guess_extension(self.frmt, False)[1:]
972
973 - def getRequest(self):
974 bbox = self.bbox if not self.swap_axes else ( 975 self.bbox[1], self.bbox[0], 976 self.bbox[3], self.bbox[2] 977 ) 978 979 params = "service=WMS&request=GetMap&version=1.3.0&" \ 980 "layers=%s&styles=%s&crs=%s&bbox=%s&" \ 981 "width=%d&height=%d&format=%s" % ( 982 ",".join(self.layers), ",".join(self.styles), self.crs, 983 ",".join(map(str, bbox)), 984 self.width, self.height, self.frmt 985 ) 986 987 if self.time: 988 params += "&time=%s" % self.time 989 990 if self.dim_band: 991 params += "&dim_band=%s" % self.dim_band 992 993 if self.httpHeaders is None: 994 return (params, "kvp") 995 else: 996 return (params, "kvp", self.httpHeaders)
997
998 -class WMS13ExceptionTestCase(ExceptionTestCase):
999 - def getExceptionCodeLocation(self):
1000 return "/{http://www.opengis.net/ogc}ServiceException/@code"
1001