#!/usr/bin/env pytest # -*- coding: utf-8 -*- ############################################################################### # $Id$ # # Project: GDAL/OGR Test Suite # Purpose: gdal2tiles.py testing # Author: Gregory Bataille # ############################################################################### # Copyright (c) 2017, Gregory Bataille # Copyright (c) 2021, Idan Miara # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. ############################################################################### import os import tempfile from unittest import TestCase, mock import pytest from osgeo import gdal from osgeo_utils import gdal2tiles class AttrDict(dict): def __init__(self, *args, **kwargs): super(AttrDict, self).__init__(*args, **kwargs) self.__dict__ = self class OptionParserInputOutputTest(TestCase): def test_vanilla_input_output(self): _, input_file = tempfile.mkstemp() output_folder = tempfile.mkdtemp() parsed_input, parsed_output, options = gdal2tiles.process_args( [input_file, output_folder] ) self.assertEqual(parsed_input, input_file) self.assertEqual(parsed_output, output_folder) self.assertNotEqual(options, {}) def test_output_folder_is_the_input_file_folder_when_none_passed(self): _, input_file = tempfile.mkstemp() _, parsed_output, _ = gdal2tiles.process_args([input_file]) self.assertEqual(parsed_output, os.path.basename(input_file)) def _asserts_exits_with_code_2(self, params): with self.assertRaises(SystemExit) as cm: gdal2tiles.process_args(params) e = cm.exception self.assertEqual(str(e), "2") def test_exits_when_0_args_passed(self): self._asserts_exits_with_code_2([]) def test_exits_when_more_than_2_free_parameters(self): self._asserts_exits_with_code_2(["input1.tiff", "input2.tiff", "output_folder"]) def test_exits_when_input_file_does_not_exist(self): self._asserts_exits_with_code_2(["foobar.tiff"]) def test_exits_when_first_param_is_not_a_file(self): folder = tempfile.gettempdir() self._asserts_exits_with_code_2([folder]) def mock_GetDriverByName_wo_webp(name, orig_GetDriverByName=gdal.GetDriverByName): if name == "WEBP": return None return orig_GetDriverByName(name) # pylint:disable=E1101 class OptionParserPostProcessingTest(TestCase): def setUp(self): self.DEFAULT_OPTIONS = { "verbose": True, "resampling": "near", "title": "", "url": "", "tiledriver": "PNG", } self.DEFAULT_ATTRDICT_OPTIONS = AttrDict(self.DEFAULT_OPTIONS) def _setup_gdal_patch(self, mock_gdal): mock_gdal.TermProgress_nocb = True mock_gdal.RegenerateOverview = True mock_gdal.GetCacheMax = lambda: 1024 * 1024 return mock_gdal def test_title_is_untouched_if_set(self): title = "fizzbuzz" self.DEFAULT_ATTRDICT_OPTIONS["title"] = title options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "bar.tiff", "baz" ) self.assertEqual(options.title, title) def test_title_default_to_input_filename_if_not_set(self): input_file = "foo/bar/fizz/buzz.tiff" options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, input_file, "baz" ) self.assertEqual(options.title, os.path.basename(input_file)) def test_url_stays_empty_if_not_passed(self): options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "baz" ) self.assertEqual(options.url, "") def test_url_ends_with_the_output_folder_last_component(self): output_folder = "foo/bar/fizz" url = "www.mysite.com/storage" self.DEFAULT_ATTRDICT_OPTIONS["url"] = url options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", output_folder ) self.assertEqual(options.url, url + "/fizz/") # With already present trailing slashes output_folder = "foo/bar/fizz/" url = "www.mysite.com/storage/" self.DEFAULT_ATTRDICT_OPTIONS["url"] = url options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", output_folder ) self.assertEqual(options.url, url + "fizz/") @mock.patch("osgeo_utils.gdal2tiles.gdal", spec=AttrDict()) def test_average_resampling_supported_with_latest_gdal(self, mock_gdal): self._setup_gdal_patch(mock_gdal) self.DEFAULT_ATTRDICT_OPTIONS["resampling"] = "average" gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) # No error means it worked as expected def test_antialias_resampling_supported_with_numpy(self): gdal2tiles.numpy_available = True self.DEFAULT_ATTRDICT_OPTIONS["resampling"] = "antialias" gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) # No error means it worked as expected def test_antialias_resampling_not_supported_wout_numpy(self): gdal2tiles.numpy_available = False if hasattr(gdal2tiles, "numpy"): del gdal2tiles.numpy self.DEFAULT_ATTRDICT_OPTIONS["resampling"] = "antialias" with self.assertRaises(SystemExit): gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) def test_zoom_option_not_specified(self): self.DEFAULT_ATTRDICT_OPTIONS["zoom"] = None options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "baz" ) self.assertEqual(options.zoom, [None, None]) def test_zoom_option_single_level(self): self.DEFAULT_ATTRDICT_OPTIONS["zoom"] = "10" options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "baz" ) self.assertEqual(options.zoom, [10, 10]) def test_zoom_option_two_levels(self): self.DEFAULT_ATTRDICT_OPTIONS["zoom"] = "14-24" options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "baz" ) self.assertEqual(options.zoom, [14, 24]) def test_zoom_option_two_levels_automatic_max(self): self.DEFAULT_ATTRDICT_OPTIONS["zoom"] = "14-" options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "baz" ) self.assertEqual(options.zoom, [14, None]) @mock.patch( "osgeo_utils.gdal2tiles.gdal.GetDriverByName", side_effect=mock_GetDriverByName_wo_webp, ) def test_tiledriver_wout_webp(self, mock_GetDriverByName_wo_webp): self.DEFAULT_ATTRDICT_OPTIONS["tiledriver"] = "WEBP" self.DEFAULT_ATTRDICT_OPTIONS["webp_quality"] = 70 with self.assertRaises(SystemExit): gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) @mock.patch( "osgeo_utils.gdal2tiles.gdal.GetDriverByName", side_effect=mock_GetDriverByName_wo_webp, ) def test_tiledriver_wout_webp_png_accepted(self, mock_GetDriverByName_wo_webp): self.DEFAULT_ATTRDICT_OPTIONS["tiledriver"] = "PNG" options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) self.assertEqual(options.tiledriver, "PNG") def test_tiledriver_webp_quality_option_valid(self): if gdal.GetDriverByName("WEBP") is None: pytest.skip() self.DEFAULT_ATTRDICT_OPTIONS["tiledriver"] = "WEBP" self.DEFAULT_ATTRDICT_OPTIONS["webp_quality"] = 1 self.DEFAULT_ATTRDICT_OPTIONS["webp_lossless"] = False options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) self.assertEqual(options.tiledriver, "WEBP") self.assertEqual(options.webp_quality, 1) self.DEFAULT_ATTRDICT_OPTIONS["webp_quality"] = 100 options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) self.assertEqual(options.tiledriver, "WEBP") self.assertEqual(options.webp_quality, 100) self.DEFAULT_ATTRDICT_OPTIONS["webp_quality"] = 50 options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) self.assertEqual(options.tiledriver, "WEBP") self.assertEqual(options.webp_quality, 50) self.DEFAULT_ATTRDICT_OPTIONS["webp_quality"] = 10.5 options = gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) self.assertEqual(options.tiledriver, "WEBP") self.assertEqual(options.webp_quality, 10) def test_tiledriver_webp_quality_option_invalid(self): if gdal.GetDriverByName("WEBP") is None: pytest.skip() self.DEFAULT_ATTRDICT_OPTIONS["tiledriver"] = "WEBP" self.DEFAULT_ATTRDICT_OPTIONS["webp_lossless"] = False self.DEFAULT_ATTRDICT_OPTIONS["webp_quality"] = -20 with self.assertRaises(SystemExit): gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) self.DEFAULT_ATTRDICT_OPTIONS["webp_quality"] = 0 with self.assertRaises(SystemExit): gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" ) self.DEFAULT_ATTRDICT_OPTIONS["webp_quality"] = 140 with self.assertRaises(SystemExit): gdal2tiles.options_post_processing( self.DEFAULT_ATTRDICT_OPTIONS, "foo.tiff", "/bar/" )