179 lines
6.9 KiB
Python
179 lines
6.9 KiB
Python
"""Module for Regression Testing Scenario Proximity Generator."""
|
|
import unittest
|
|
import tempfile
|
|
import shutil
|
|
import os
|
|
|
|
import pandas
|
|
from osgeo import gdal
|
|
|
|
gdal.UseExceptions()
|
|
TEST_DATA_DIR = os.path.join(
|
|
os.path.dirname(__file__), '..', 'data', 'invest-test-data',
|
|
'scenario_gen_proximity')
|
|
|
|
|
|
class ScenarioProximityTests(unittest.TestCase):
|
|
"""Tests for the Scenario Proximity Generator."""
|
|
|
|
def setUp(self):
|
|
"""Overriding setUp function to create temp workspace directory."""
|
|
# this lets us delete the workspace after its done no matter the
|
|
# the rest result
|
|
self.workspace_dir = tempfile.mkdtemp()
|
|
|
|
def tearDown(self):
|
|
"""Overriding tearDown function to remove temporary directory."""
|
|
shutil.rmtree(self.workspace_dir)
|
|
|
|
@staticmethod
|
|
def generate_base_args(workspace_dir):
|
|
"""Generate an args list consistent across all regression tests."""
|
|
args = {
|
|
'aoi_path': os.path.join(
|
|
TEST_DATA_DIR, 'input', 'scenario_proximity_aoi.gpkg'),
|
|
'area_to_convert': '3218.0',
|
|
'base_lulc_path': os.path.join(
|
|
TEST_DATA_DIR, 'input', 'clipped_lulc.tif'),
|
|
'workspace_dir': workspace_dir,
|
|
'convertible_landcover_codes': '1 2 3 4 5',
|
|
'focal_landcover_codes': '1 2 3 4 5',
|
|
'n_fragmentation_steps': '1',
|
|
'replacement_lucode': '12',
|
|
'n_workers': '-1',
|
|
}
|
|
return args
|
|
|
|
def test_scenario_gen_regression(self):
|
|
"""Scenario Gen Proximity: regression testing all functionality."""
|
|
from natcap.invest import scenario_gen_proximity
|
|
|
|
args = ScenarioProximityTests.generate_base_args(self.workspace_dir)
|
|
args['convert_farthest_from_edge'] = True
|
|
args['convert_nearest_to_edge'] = True
|
|
|
|
scenario_gen_proximity.execute(args)
|
|
ScenarioProximityTests._test_same_files(
|
|
os.path.join(
|
|
TEST_DATA_DIR, 'expected_file_list_regression.txt'),
|
|
args['workspace_dir'])
|
|
|
|
base_table = pandas.read_csv(
|
|
os.path.join(self.workspace_dir, 'farthest_from_edge.csv'))
|
|
expected_table = pandas.read_csv(
|
|
os.path.join(
|
|
TEST_DATA_DIR, 'farthest_from_edge_regression.csv'))
|
|
pandas.testing.assert_frame_equal(base_table, expected_table)
|
|
|
|
base_table = pandas.read_csv(
|
|
os.path.join(self.workspace_dir, 'nearest_to_edge.csv'))
|
|
expected_table = pandas.read_csv(
|
|
os.path.join(
|
|
TEST_DATA_DIR, 'nearest_to_edge_regression.csv'))
|
|
pandas.testing.assert_frame_equal(base_table, expected_table)
|
|
|
|
def test_scenario_gen_farthest(self):
|
|
"""Scenario Gen Proximity: testing small far functionality."""
|
|
from natcap.invest import scenario_gen_proximity
|
|
|
|
args = ScenarioProximityTests.generate_base_args(self.workspace_dir)
|
|
args['convert_farthest_from_edge'] = True
|
|
args['convert_nearest_to_edge'] = False
|
|
# running without an AOI
|
|
del args['aoi_path']
|
|
scenario_gen_proximity.execute(args)
|
|
ScenarioProximityTests._test_same_files(
|
|
os.path.join(
|
|
TEST_DATA_DIR, 'expected_file_list_farthest.txt'),
|
|
args['workspace_dir'])
|
|
|
|
model_df = pandas.read_csv(
|
|
os.path.join(self.workspace_dir, 'farthest_from_edge.csv'))
|
|
reg_df = pandas.read_csv(
|
|
os.path.join(TEST_DATA_DIR, 'farthest_from_edge_farthest.csv'))
|
|
pandas.testing.assert_frame_equal(model_df, reg_df)
|
|
|
|
def test_scenario_gen_no_scenario(self):
|
|
"""Scenario Gen Proximity: no scenario should raise an exception."""
|
|
from natcap.invest import scenario_gen_proximity
|
|
|
|
args = ScenarioProximityTests.generate_base_args(self.workspace_dir)
|
|
args['convert_farthest_from_edge'] = False
|
|
args['convert_nearest_to_edge'] = False
|
|
|
|
# both scenarios false should raise a value error
|
|
with self.assertRaises(ValueError):
|
|
scenario_gen_proximity.execute(args)
|
|
|
|
@staticmethod
|
|
def _test_same_files(base_list_path, directory_path):
|
|
"""Assert files in `base_list_path` are in `directory_path`.
|
|
|
|
Args:
|
|
base_list_path (string): a path to a file that has one relative
|
|
file path per line.
|
|
directory_path (string): a path to a directory whose contents will
|
|
be checked against the files listed in `base_list_file`
|
|
|
|
Returns:
|
|
None
|
|
|
|
Raises:
|
|
AssertionError when there are files listed in `base_list_file`
|
|
that don't exist in the directory indicated by `path`
|
|
"""
|
|
missing_files = []
|
|
with open(base_list_path, 'r') as file_list:
|
|
for file_path in file_list:
|
|
full_path = os.path.join(directory_path, file_path.rstrip())
|
|
if full_path == '':
|
|
continue
|
|
if not os.path.isfile(full_path):
|
|
missing_files.append(full_path)
|
|
if len(missing_files) > 0:
|
|
raise AssertionError(
|
|
"The following files were expected but not found: " +
|
|
'\n'.join(missing_files))
|
|
|
|
|
|
class ScenarioGenValidationTests(unittest.TestCase):
|
|
"""Tests for the Scenario Generator MODEL_SPEC and validation."""
|
|
|
|
def setUp(self):
|
|
"""Initiate list of required keys."""
|
|
self.base_required_keys = [
|
|
'focal_landcover_codes',
|
|
'replacement_lucode',
|
|
'workspace_dir',
|
|
'n_fragmentation_steps',
|
|
'convertible_landcover_codes',
|
|
'area_to_convert',
|
|
'base_lulc_path',
|
|
'convert_nearest_to_edge',
|
|
'convert_farthest_from_edge'
|
|
]
|
|
|
|
def test_missing_keys(self):
|
|
"""SG Validate: assert missing required keys."""
|
|
from natcap.invest import scenario_gen_proximity
|
|
from natcap.invest import validation
|
|
|
|
# empty args dict.
|
|
validation_errors = scenario_gen_proximity.validate({})
|
|
invalid_keys = validation.get_invalid_keys(validation_errors)
|
|
expected_missing_keys = set(self.base_required_keys)
|
|
self.assertEqual(invalid_keys, expected_missing_keys)
|
|
|
|
def test_invalid_conversion_methods(self):
|
|
"""SG Validate: assert message if both conversion methods false."""
|
|
from natcap.invest import scenario_gen_proximity
|
|
|
|
validation_errors = scenario_gen_proximity.validate(
|
|
{'convert_nearest_to_edge': False,
|
|
'convert_farthest_from_edge': False})
|
|
actual_messages = set()
|
|
for keys, error_strings in validation_errors:
|
|
actual_messages.add(error_strings)
|
|
self.assertTrue(scenario_gen_proximity.MISSING_CONVERT_OPTION_MSG
|
|
in actual_messages)
|