Skip to content
Snippets Groups Projects
test_processing.py 13.2 KiB
Newer Older
Kien Le's avatar
Kien Le committed
from tempfile import TemporaryDirectory, NamedTemporaryFile
from pathlib import Path

from unittest import TestCase
from unittest.mock import patch
Lan Dam's avatar
Lan Dam committed
from contextlib import redirect_stdout
import io
Kien Le's avatar
Kien Le committed

from sohstationviewer.controller.processing import (
    loadData,
    readChannels,
    detectDataType,
    getDataTypeFromFile
)
from sohstationviewer.database.extractData import signatureChannels
from PySide2 import QtWidgets
from sohstationviewer.model.mseed.mseed import MSeed
from sohstationviewer.model.reftek.reftek import RT130

TEST_DATA_DIR = Path(__file__).resolve().parent.parent.joinpath('test_data')
rt130_dir = TEST_DATA_DIR.joinpath('RT130-sample/2017149.92EB/2017150')
q330_dir = TEST_DATA_DIR.joinpath('Q330-sample/day_vols_AX08')
centaur_dir = TEST_DATA_DIR.joinpath('Centaur-sample/SOH')
pegasus_dir = TEST_DATA_DIR.joinpath('Pegasus-sample/Pegasus_SVC4/soh')


class TestLoadDataAndReadChannels(TestCase):
    """Test suite for loadData and readChannels."""

    def setUp(self) -> None:
        """Set up test fixtures."""
        patcher = patch.object(QtWidgets, 'QTextBrowser')
        self.addCleanup(patcher.stop)
        self.widget_stub = patcher.start()

        # The actual value can be 'Q330', 'Centaur', or 'Pegasus'.
        # The code that uses this string only cares that it is not 'RT130',
        # though, so we are setting it to a stub value.
        self.mseed_dtype = 'MSeed'

    def test_load_data_rt130_good_dir(self):
        """
        Test basic functionality of loadData - the given directory can be
        loaded without issues. Test RT130.
        """
        self.assertIsInstance(
            loadData('RT130', self.widget_stub, [rt130_dir]),
            RT130
        )

    def test_load_data_mseed_q330_good_data_dir(self):
        """
        Test basic functionality of loadData - the given directory can be
        loaded without issues. Test MSeed.
        """
        self.assertIsInstance(
            loadData(self.mseed_dtype, self.widget_stub, [q330_dir]),
            MSeed
        )
        self.assertIsInstance(
            loadData(self.mseed_dtype, self.widget_stub, [centaur_dir]),
            MSeed
        )
        self.assertIsInstance(
            loadData(self.mseed_dtype, self.widget_stub, [pegasus_dir]),
            MSeed
        )

Kien Le's avatar
Kien Le committed
        """Test basic functionality of loadData - no directory was given."""
        no_dir_given = []
        self.assertIsNone(loadData('RT130', self.widget_stub, no_dir_given))
        self.assertIsNone(
            loadData(self.mseed_dtype, self.widget_stub, no_dir_given))

    def test_load_data_dir_does_not_exist(self):
Kien Le's avatar
Kien Le committed
        """
        Test basic functionality of loadData - the given directory does not
        exist.
        """
        empty_name_dir = ['']
        non_existent_dir = ['dir_that_does_not_exist']

        self.assertIsNone(
            loadData('RT130', self.widget_stub, empty_name_dir))
        self.assertIsNone(
            loadData('RT130', self.widget_stub, non_existent_dir))
Kien Le's avatar
Kien Le committed

        self.assertIsNone(
            loadData(self.mseed_dtype, self.widget_stub, empty_name_dir))
        self.assertIsNone(
            loadData(self.mseed_dtype, self.widget_stub, non_existent_dir))

Kien Le's avatar
Kien Le committed
        """
        Test basic functionality of loadData - the given directory is empty.
        """
        with TemporaryDirectory() as empty_dir:
            self.assertIsNone(
                loadData('RT130', self.widget_stub, [empty_dir]))
Kien Le's avatar
Kien Le committed
            self.assertIsNone(
                loadData(self.mseed_dtype, self.widget_stub, [empty_dir]))

    def test_load_data_empty_data_dir(self):
Kien Le's avatar
Kien Le committed
        """
        Test basic functionality of loadData - the given directory
        contains a data folder but no data file.
        """
        with TemporaryDirectory() as outer_dir:
            with TemporaryDirectory(dir=outer_dir) as data_dir:
                self.assertIsNone(
                    loadData('RT130', self.widget_stub, [data_dir]))
Kien Le's avatar
Kien Le committed
                self.assertIsNone(
                    loadData(self.mseed_dtype, self.widget_stub, [outer_dir]))

    def test_load_data_data_type_mismatch(self):
        """
        Test basic functionality of loadData - the data type given does not
        match the type of the data contained in the given directory.
        """
        self.assertIsNone(
            loadData('RT130', self.widget_stub, [q330_dir]))
Kien Le's avatar
Kien Le committed
        self.assertIsNone(
            loadData(self.mseed_dtype, self.widget_stub, [rt130_dir]))

Lan Dam's avatar
Lan Dam committed
    def test_load_data_data_traceback_error(self):
        """
        Test basic functionality of loadData - when there is an error
        on loading data, the traceback info will be printed out
        """
        f = io.StringIO()
        with redirect_stdout(f):
            self.assertIsNone(loadData('RT130', None, [q330_dir]))
        output = f.getvalue()
        self.assertIn(
            f"Warning: Dir {q330_dir} "
            f"can't be read due to error: Traceback",
            output
        )
        with redirect_stdout(f):
            self.assertIsNone(
                loadData(self.mseed_dtype, None, [rt130_dir]))
        output = f.getvalue()
        self.assertIn(
            f"Warning: Dir {rt130_dir} "
            f"can't be read due to error: Traceback",
            output
        )

Kien Le's avatar
Kien Le committed
    def test_read_channels_mseed_dir(self):
        """
        Test basic functionality of loadData - the given directory contains
        MSeed data.
        """
        q330_channels = {'VKI', 'VM1'}
        self.assertSetEqual(readChannels(self.widget_stub, [q330_dir]),
                            q330_channels)

        centaur_channels = {'VDT', 'VM3', 'EX3', 'GEL', 'VEC', 'EX2', 'LCE',
                            'EX1', 'GLA', 'LCQ', 'GPL', 'GNS', 'GST', 'VCO',
                            'GAN', 'GLO', 'VPB', 'VEI', 'VM2', 'VM1'}
        self.assertSetEqual(readChannels(self.widget_stub, [centaur_dir]),
                            centaur_channels)

        pegasus_channels = {'VDT', 'VM1', 'VE1'}
        self.assertSetEqual(readChannels(self.widget_stub, [pegasus_dir]),
                            pegasus_channels)

    def test_read_channels_rt130_dir(self):
        """
        Test basic functionality of loadData - the given directory contains
        RT130 data.
        """
        with self.assertRaises(Exception):
            readChannels(self.widget_stub, [rt130_dir])

    def test_read_channels_no_dir(self):
        """
        Test basic functionality of readChannels - no directory was given.
        """
        no_dir = []
        with self.assertRaises(Exception):
            readChannels(self.widget_stub, no_dir)

    def test_read_channels_dir_does_not_exist(self):
        """
        Test basic functionality of readChannels - the given directory does not
        exist.
        """
        empty_name_dir = ['']
        non_existent_dir = ['non_existent_dir']
        with self.assertRaises(Exception):
            readChannels(self.widget_stub, empty_name_dir)
        with self.assertRaises(Exception):
            readChannels(self.widget_stub, non_existent_dir)

    def test_read_channels_empty_dir(self):
        """
        Test basic functionality of readChannels - the given directory is
        empty.
        """
        with TemporaryDirectory() as empty_dir:
            with self.assertRaises(Exception):
                readChannels(self.widget_stub, [empty_dir])

    def test_read_channels_empty_data_dir(self):
        """
        Test basic functionality of readChannels - the given directory
        contains a data folder but no data file.
        """
        with TemporaryDirectory() as outer_dir:
            with TemporaryDirectory(dir=outer_dir):
                with self.assertRaises(Exception):
                    readChannels(self.widget_stub, [outer_dir])


class TestDetectDataType(TestCase):
    """Test suite for detectDataType and getDataTypeFromFile functions."""

    def setUp(self) -> None:
        """Set up text fixtures."""
        widget_patcher = patch.object(QtWidgets, 'QTextBrowser')
        self.addCleanup(widget_patcher.stop)
        self.widget_stub = widget_patcher.start()

        func_patcher = patch('sohstationviewer.controller.processing.'
                             'getDataTypeFromFile')
        self.addCleanup(func_patcher.stop)
        self.mock_get_data_type_from_file = func_patcher.start()

        self.dir1 = TemporaryDirectory()
        self.dir2 = TemporaryDirectory()
        self.file1 = NamedTemporaryFile(dir=self.dir1.name)
        self.file2 = NamedTemporaryFile(dir=self.dir2.name)

    def tearDown(self) -> None:
        """Teardown text fixtures."""
        del self.file1, self.file2
        self.dir1.cleanup()
        self.dir2.cleanup()

    def test_one_directory_not_unknown_data_type(self):
        """
        Test basic functionality of detectDataType - only one directory was
        given and the data type it contains can be detected.
        """
        expected_data_type = ('RT130', '_')
        self.mock_get_data_type_from_file.return_value = expected_data_type

        self.assertEqual(
            detectDataType(self.widget_stub, [self.dir1.name]),
            expected_data_type[0]
        )

    def test_same_data_type_and_channel(self):
        """
        Test basic functionality of detectDataType - the given directories
        contain the same data type and the data type was detected using the
        same channel.
        """
        expected_data_type = ('RT130', '_')
        self.mock_get_data_type_from_file.return_value = expected_data_type

        self.assertEqual(
            detectDataType(self.widget_stub, [self.dir1.name, self.dir2.name]),
            expected_data_type[0]
        )

    def test_same_data_type_different_channel(self):
        """
        Test basic functionality of detectDataType - the given directories
        contain the same data type but the data type was detected using
        different channels.
        """
        returned_data_types = [('Q330', 'OCF'), ('Q330', 'VEP')]
        self.mock_get_data_type_from_file.side_effect = returned_data_types

        self.assertEqual(
            detectDataType(self.widget_stub, [self.dir1.name, self.dir2.name]),
            returned_data_types[0][0]
        )

    def test_different_data_types(self):
        """
        Test basic functionality of detectDataType - the given directories
        contain different data types.
        """
        returned_data_types = [('RT130', '_'), ('Q330', 'VEP')]
        self.mock_get_data_type_from_file.side_effect = returned_data_types

        self.assertIsNone(
            detectDataType(self.widget_stub, [self.dir1.name, self.dir2.name])
        )

    def test_unknown_data_type(self):
        """
        Test basic functionality of detectDataType - can't detect any data
        type.
        """
        unknown_data_type = ('Unknown', '_')
        self.mock_get_data_type_from_file.return_value = unknown_data_type

        self.assertIsNone(detectDataType(self.widget_stub, [self.dir1.name]))


class TestGetDataTypeFromFile(TestCase):
    """Test suite for getDataTypeFromFile"""
    def test_rt130_data(self):
        """
        Test basic functionality of getDataTypeFromFile - given file contains
        RT130 data.
        """
        rt130_file = Path(rt130_dir).joinpath(
            '92EB/0/000000000_00000000')
        expected_data_type = ('RT130', '_')
        self.assertTupleEqual(
            getDataTypeFromFile(rt130_file, signatureChannels()),
            expected_data_type
        )

    def test_cannot_detect_data_type(self):
        """
        Test basic functionality of getDataTypeFromFile - cannot detect data
        type contained in given file.
        """
        test_file = NamedTemporaryFile()
        self.assertIsNone(
            getDataTypeFromFile(test_file.name, signatureChannels()))

    def test_mseed_data(self):
        """
        Test basic functionality of getDataTypeFromFile - given file contains
        MSeed data.
        """
        q330_file = q330_dir.joinpath('AX08.XA..VKI.2021.186')
        centaur_file = centaur_dir.joinpath(
            'XX.3734.SOH.centaur-3_3734..20180817_000000.miniseed.miniseed')
        pegasus_file = pegasus_dir.joinpath(
            '2020/XX/KC01/VE1.D/XX.KC01..VE1.D.2020.129')
        q330_data_type = ('Q330', 'VKI')
        centaur_data_type = ('Centaur', 'GEL')
        pegasus_data_type = ('Pegasus', 'VE1')

        sig_chan = signatureChannels()

        self.assertTupleEqual(getDataTypeFromFile(q330_file, sig_chan),
                              q330_data_type)
        self.assertTupleEqual(getDataTypeFromFile(centaur_file, sig_chan),
                              centaur_data_type)
        self.assertTupleEqual(getDataTypeFromFile(pegasus_file, sig_chan),
                              pegasus_data_type)

    def test_file_does_not_exist(self):
        """
        Test basic functionality of getDataTypeFromFile - given file does not
        exist.
        """
        empty_name_file = ''
        non_existent_file = 'non_existent_dir'
        with self.assertRaises(FileNotFoundError):
            getDataTypeFromFile(empty_name_file, signatureChannels())
        with self.assertRaises(FileNotFoundError):
            getDataTypeFromFile(non_existent_file, signatureChannels())