# /*##########################################################################
# Copyright (C) 2016-2022 European Synchrotron Radiation Facility
#
# 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.
#
# ############################################################################*/
"""Tests for NXdata parsing"""

__authors__ = ["P. Knobel"]
__license__ = "MIT"
__date__ = "24/03/2020"


import tempfile
import unittest

import h5py
import numpy
import pytest

from .. import nxdata
from ..dictdump import dicttoh5


text_dtype = h5py.special_dtype(vlen=str)


class TestNXdata(unittest.TestCase):
    def setUp(self):
        tmp = tempfile.NamedTemporaryFile(
            prefix="nxdata_examples_", suffix=".h5", delete=True
        )
        tmp.file.close()
        self.h5fname = tmp.name
        self.h5f = h5py.File(tmp.name, "w")

        # SCALARS
        g0d = self.h5f.create_group("scalars")

        g0d0 = g0d.create_group("0D_scalar")
        g0d0.attrs["NX_class"] = "NXdata"
        g0d0.attrs["signal"] = "scalar"
        g0d0.create_dataset("scalar", data=10)
        g0d0.create_dataset("scalar_errors", data=0.1)

        g0d1 = g0d.create_group("2D_scalars")
        g0d1.attrs["NX_class"] = "NXdata"
        g0d1.attrs["signal"] = "scalars"
        ds = g0d1.create_dataset("scalars", data=numpy.arange(3 * 10).reshape((3, 10)))
        ds.attrs["interpretation"] = "scalar"

        g0d1 = g0d.create_group("4D_scalars")
        g0d1.attrs["NX_class"] = "NXdata"
        g0d1.attrs["signal"] = "scalars"
        ds = g0d1.create_dataset(
            "scalars", data=numpy.arange(2 * 2 * 3 * 10).reshape((2, 2, 3, 10))
        )
        ds.attrs["interpretation"] = "scalar"

        # SPECTRA
        g1d = self.h5f.create_group("spectra")

        g1d0 = g1d.create_group("1D_spectrum")
        g1d0.attrs["NX_class"] = "NXdata"
        g1d0.attrs["signal"] = "count"
        g1d0.attrs["auxiliary_signals"] = numpy.array(
            ["count2", "count3"], dtype=text_dtype
        )
        g1d0.attrs["axes"] = "energy_calib"
        g1d0.attrs["uncertainties"] = numpy.array(
            [
                "energy_errors",
            ],
            dtype=text_dtype,
        )
        g1d0.create_dataset("count", data=numpy.arange(10))
        g1d0.create_dataset("count2", data=0.5 * numpy.arange(10))
        d = g1d0.create_dataset("count3", data=0.4 * numpy.arange(10))
        d.attrs["long_name"] = "3rd counter"
        g1d0.create_dataset("title", data="Title as dataset (like nexpy)")
        g1d0.create_dataset("energy_calib", data=(10, 5))  # 10 * idx + 5
        g1d0.create_dataset("energy_errors", data=3.14 * numpy.random.rand(10))

        g1d1 = g1d.create_group("2D_spectra")
        g1d1.attrs["NX_class"] = "NXdata"
        g1d1.attrs["signal"] = "counts"
        ds = g1d1.create_dataset("counts", data=numpy.arange(3 * 10).reshape((3, 10)))
        ds.attrs["interpretation"] = "spectrum"

        g1d2 = g1d.create_group("4D_spectra")
        g1d2.attrs["NX_class"] = "NXdata"
        g1d2.attrs["signal"] = "counts"
        g1d2.attrs["axes"] = numpy.array(
            [
                "energy",
            ],
            dtype=text_dtype,
        )
        ds = g1d2.create_dataset(
            "counts", data=numpy.arange(2 * 2 * 3 * 10).reshape((2, 2, 3, 10))
        )
        ds.attrs["interpretation"] = "spectrum"
        ds = g1d2.create_dataset("errors", data=4.5 * numpy.random.rand(2, 2, 3, 10))
        ds = g1d2.create_dataset(
            "energy", data=5 + 10 * numpy.arange(15), shuffle=True, compression="gzip"
        )
        ds.attrs["long_name"] = "Calibrated energy"
        ds.attrs["first_good"] = 3
        ds.attrs["last_good"] = 12
        g1d2.create_dataset("energy_errors", data=10 * numpy.random.rand(15))

        # IMAGES
        g2d = self.h5f.create_group("images")

        g2d0 = g2d.create_group("2D_regular_image")
        g2d0.attrs["NX_class"] = "NXdata"
        g2d0.attrs["signal"] = "image"
        g2d0.attrs["auxiliary_signals"] = "image2"
        g2d0.attrs["axes"] = numpy.array(
            ["rows_calib", "columns_coordinates"], dtype=text_dtype
        )
        g2d0.create_dataset("image", data=numpy.arange(4 * 6).reshape((4, 6)))
        g2d0.create_dataset("image2", data=numpy.arange(4 * 6).reshape((4, 6)))
        ds = g2d0.create_dataset("rows_calib", data=(10, 5))
        ds.attrs["long_name"] = "Calibrated Y"
        g2d0.create_dataset("columns_coordinates", data=0.5 + 0.02 * numpy.arange(6))

        g2d1 = g2d.create_group("2D_irregular_data")
        g2d1.attrs["NX_class"] = "NXdata"
        g2d1.attrs["signal"] = "data"
        g2d1.attrs["title"] = "Title as group attr"
        g2d1.attrs["axes"] = numpy.array(
            ["rows_coordinates", "columns_coordinates"], dtype=text_dtype
        )
        g2d1.create_dataset("data", data=numpy.arange(64 * 128).reshape((64, 128)))
        g2d1.create_dataset(
            "rows_coordinates", data=numpy.arange(64) + numpy.random.rand(64)
        )
        g2d1.create_dataset(
            "columns_coordinates", data=numpy.arange(128) + 2.5 * numpy.random.rand(128)
        )

        g2d2 = g2d.create_group("3D_images")
        g2d2.attrs["NX_class"] = "NXdata"
        g2d2.attrs["signal"] = "images"
        ds = g2d2.create_dataset(
            "images", data=numpy.arange(2 * 4 * 6).reshape((2, 4, 6))
        )
        ds.attrs["interpretation"] = "image"

        g2d3 = g2d.create_group("5D_images")
        g2d3.attrs["NX_class"] = "NXdata"
        g2d3.attrs["signal"] = "images"
        g2d3.attrs["axes"] = numpy.array(
            ["rows_coordinates", "columns_coordinates"], dtype=text_dtype
        )
        ds = g2d3.create_dataset(
            "images", data=numpy.arange(2 * 2 * 2 * 4 * 6).reshape((2, 2, 2, 4, 6))
        )
        ds.attrs["interpretation"] = "image"
        g2d3.create_dataset("rows_coordinates", data=5 + 10 * numpy.arange(4))
        g2d3.create_dataset("columns_coordinates", data=0.5 + 0.02 * numpy.arange(6))

        g2d4 = g2d.create_group("RGBA_image")
        g2d4.attrs["NX_class"] = "NXdata"
        g2d4.attrs["signal"] = "image"
        g2d4.attrs["axes"] = numpy.array(
            ["rows_calib", "columns_coordinates"], dtype=text_dtype
        )
        rgba_image = numpy.linspace(0, 1, num=7 * 8 * 3).reshape((7, 8, 3))
        rgba_image[:, :, 1] = (
            1 - rgba_image[:, :, 1]
        )  # invert G channel to add some color
        ds = g2d4.create_dataset("image", data=rgba_image)
        ds.attrs["interpretation"] = "rgba-image"
        ds = g2d4.create_dataset("rows_calib", data=(10, 5))
        ds.attrs["long_name"] = "Calibrated Y"
        g2d4.create_dataset("columns_coordinates", data=0.5 + 0.02 * numpy.arange(8))

        # SCATTER
        g = self.h5f.create_group("scatters")

        gd0 = g.create_group("x_y_scatter")
        gd0.attrs["NX_class"] = "NXdata"
        gd0.attrs["signal"] = "y"
        gd0.attrs["axes"] = numpy.array(
            [
                "x",
            ],
            dtype=text_dtype,
        )
        gd0.create_dataset("y", data=numpy.random.rand(128) - 0.5)
        gd0.create_dataset("x", data=2 * numpy.random.rand(128))
        gd0.create_dataset("x_errors", data=0.05 * numpy.random.rand(128))
        gd0.create_dataset("errors", data=0.05 * numpy.random.rand(128))

        gd1 = g.create_group("x_y_value_scatter")
        gd1.attrs["NX_class"] = "NXdata"
        gd1.attrs["signal"] = "values"
        gd1.attrs["axes"] = numpy.array(["x", "y"], dtype=text_dtype)
        gd1.create_dataset("values", data=3.14 * numpy.random.rand(128))
        gd1.create_dataset("y", data=numpy.random.rand(128))
        gd1.create_dataset("y_errors", data=0.02 * numpy.random.rand(128))
        gd1.create_dataset("x", data=numpy.random.rand(128))
        gd1.create_dataset("x_errors", data=0.02 * numpy.random.rand(128))

    def tearDown(self):
        self.h5f.close()

    def testValidity(self):
        for group in self.h5f:
            for subgroup in self.h5f[group]:
                self.assertTrue(
                    nxdata.is_valid_nxdata(self.h5f[group][subgroup]),
                    "%s/%s not found to be a valid NXdata group" % (group, subgroup),
                )

    def testScalars(self):
        nxd = nxdata.NXdata(self.h5f["scalars/0D_scalar"])
        self.assertTrue(nxd.signal_is_0d)
        self.assertEqual(nxd.signal[()], 10)
        self.assertEqual(nxd.axes_names, [])
        self.assertEqual(nxd.axes_dataset_names, [])
        self.assertEqual(nxd.axes, [])
        self.assertIsNotNone(nxd.errors)
        self.assertFalse(nxd.is_scatter or nxd.is_x_y_value_scatter)
        self.assertIsNone(nxd.interpretation)

        nxd = nxdata.NXdata(self.h5f["scalars/2D_scalars"])
        self.assertTrue(nxd.signal_is_2d)
        self.assertEqual(nxd.signal[1, 2], 12)
        self.assertEqual(nxd.axes_names, [None, None])
        self.assertEqual(nxd.axes_dataset_names, [None, None])
        self.assertEqual(nxd.axes, [None, None])
        self.assertIsNone(nxd.errors)
        self.assertFalse(nxd.is_scatter or nxd.is_x_y_value_scatter)
        self.assertEqual(nxd.interpretation, "scalar")

        nxd = nxdata.NXdata(self.h5f["scalars/4D_scalars"])
        self.assertFalse(
            nxd.signal_is_0d or nxd.signal_is_1d or nxd.signal_is_2d or nxd.signal_is_3d
        )
        self.assertEqual(nxd.signal[1, 0, 1, 4], 74)
        self.assertEqual(nxd.axes_names, [None, None, None, None])
        self.assertEqual(nxd.axes_dataset_names, [None, None, None, None])
        self.assertEqual(nxd.axes, [None, None, None, None])
        self.assertIsNone(nxd.errors)
        self.assertFalse(nxd.is_scatter or nxd.is_x_y_value_scatter)
        self.assertEqual(nxd.interpretation, "scalar")

    def testSpectra(self):
        nxd = nxdata.NXdata(self.h5f["spectra/1D_spectrum"])
        self.assertTrue(nxd.signal_is_1d)
        self.assertTrue(nxd.is_curve)
        self.assertTrue(numpy.array_equal(numpy.array(nxd.signal), numpy.arange(10)))
        self.assertEqual(nxd.axes_names, ["energy_calib"])
        self.assertEqual(nxd.axes_dataset_names, ["energy_calib"])
        self.assertEqual(nxd.axes[0][0], 10)
        self.assertEqual(nxd.axes[0][1], 5)
        self.assertIsNone(nxd.errors)
        self.assertFalse(nxd.is_scatter or nxd.is_x_y_value_scatter)
        self.assertIsNone(nxd.interpretation)
        self.assertEqual(nxd.title, "Title as dataset (like nexpy)")

        self.assertEqual(nxd.auxiliary_signals_dataset_names, ["count2", "count3"])
        self.assertEqual(nxd.auxiliary_signals_names, ["count2", "3rd counter"])
        self.assertAlmostEqual(
            nxd.auxiliary_signals[1][2], 0.8
        )  # numpy.arange(10) * 0.4

        nxd = nxdata.NXdata(self.h5f["spectra/2D_spectra"])
        self.assertTrue(nxd.signal_is_2d)
        self.assertTrue(nxd.is_curve)
        self.assertEqual(nxd.axes_names, [None, None])
        self.assertEqual(nxd.axes_dataset_names, [None, None])
        self.assertEqual(nxd.axes, [None, None])
        self.assertIsNone(nxd.errors)
        self.assertFalse(nxd.is_scatter or nxd.is_x_y_value_scatter)
        self.assertEqual(nxd.interpretation, "spectrum")

        nxd = nxdata.NXdata(self.h5f["spectra/4D_spectra"])
        self.assertFalse(
            nxd.signal_is_0d or nxd.signal_is_1d or nxd.signal_is_2d or nxd.signal_is_3d
        )
        self.assertTrue(nxd.is_curve)
        self.assertEqual(nxd.axes_names, [None, None, None, "Calibrated energy"])
        self.assertEqual(nxd.axes_dataset_names, [None, None, None, "energy"])
        self.assertEqual(nxd.axes[:3], [None, None, None])
        self.assertEqual(nxd.axes[3].shape, (10,))  # dataset shape (15, ) sliced [3:12]
        self.assertIsNotNone(nxd.errors)
        self.assertEqual(nxd.errors.shape, (2, 2, 3, 10))
        self.assertFalse(nxd.is_scatter or nxd.is_x_y_value_scatter)
        self.assertEqual(nxd.interpretation, "spectrum")
        self.assertEqual(nxd.get_axis_errors("energy").shape, (10,))
        # test getting axis errors by long_name
        self.assertTrue(
            numpy.array_equal(
                nxd.get_axis_errors("Calibrated energy"), nxd.get_axis_errors("energy")
            )
        )
        self.assertTrue(
            numpy.array_equal(
                nxd.get_axis_errors(b"Calibrated energy"), nxd.get_axis_errors("energy")
            )
        )

    def testImages(self):
        nxd = nxdata.NXdata(self.h5f["images/2D_regular_image"])
        self.assertTrue(nxd.signal_is_2d)
        self.assertTrue(nxd.is_image)
        self.assertEqual(nxd.axes_names, ["Calibrated Y", "columns_coordinates"])
        self.assertEqual(
            list(nxd.axes_dataset_names), ["rows_calib", "columns_coordinates"]
        )
        self.assertIsNone(nxd.errors)
        self.assertFalse(nxd.is_scatter or nxd.is_x_y_value_scatter)
        self.assertIsNone(nxd.interpretation)
        self.assertEqual(len(nxd.auxiliary_signals), 1)
        self.assertEqual(nxd.auxiliary_signals_names, ["image2"])

        nxd = nxdata.NXdata(self.h5f["images/2D_irregular_data"])
        self.assertTrue(nxd.signal_is_2d)
        self.assertTrue(nxd.is_image)

        self.assertEqual(nxd.axes_dataset_names, nxd.axes_names)
        self.assertEqual(
            list(nxd.axes_dataset_names), ["rows_coordinates", "columns_coordinates"]
        )
        self.assertEqual(len(nxd.axes), 2)
        self.assertIsNone(nxd.errors)
        self.assertFalse(nxd.is_scatter or nxd.is_x_y_value_scatter)
        self.assertIsNone(nxd.interpretation)
        self.assertEqual(nxd.title, "Title as group attr")

        nxd = nxdata.NXdata(self.h5f["images/5D_images"])
        self.assertTrue(nxd.is_image)
        self.assertFalse(
            nxd.signal_is_0d or nxd.signal_is_1d or nxd.signal_is_2d or nxd.signal_is_3d
        )
        self.assertEqual(
            nxd.axes_names,
            [None, None, None, "rows_coordinates", "columns_coordinates"],
        )
        self.assertEqual(
            nxd.axes_dataset_names,
            [None, None, None, "rows_coordinates", "columns_coordinates"],
        )
        self.assertIsNone(nxd.errors)
        self.assertFalse(nxd.is_scatter or nxd.is_x_y_value_scatter)
        self.assertEqual(nxd.interpretation, "image")

        nxd = nxdata.NXdata(self.h5f["images/RGBA_image"])
        self.assertTrue(nxd.is_image)
        self.assertEqual(nxd.interpretation, "rgba-image")
        self.assertTrue(nxd.signal_is_3d)
        self.assertEqual(nxd.axes_names, ["Calibrated Y", "columns_coordinates", None])
        self.assertEqual(
            list(nxd.axes_dataset_names), ["rows_calib", "columns_coordinates", None]
        )

    def testScatters(self):
        nxd = nxdata.NXdata(self.h5f["scatters/x_y_scatter"])
        self.assertTrue(nxd.signal_is_1d)
        self.assertEqual(nxd.axes_names, ["x"])
        self.assertEqual(nxd.axes_dataset_names, ["x"])
        self.assertIsNotNone(nxd.errors)
        self.assertEqual(nxd.get_axis_errors("x").shape, (128,))
        self.assertTrue(nxd.is_scatter)
        self.assertFalse(nxd.is_x_y_value_scatter)
        self.assertIsNone(nxd.interpretation)

        nxd = nxdata.NXdata(self.h5f["scatters/x_y_value_scatter"])
        self.assertFalse(nxd.signal_is_1d)
        self.assertTrue(nxd.axes_dataset_names, nxd.axes_names)
        self.assertEqual(nxd.axes_dataset_names, ["x", "y"])
        self.assertEqual(nxd.get_axis_errors("x").shape, (128,))
        self.assertEqual(nxd.get_axis_errors("y").shape, (128,))
        self.assertEqual(len(nxd.axes), 2)
        self.assertIsNone(nxd.errors)
        self.assertTrue(nxd.is_scatter)
        self.assertTrue(nxd.is_x_y_value_scatter)
        self.assertIsNone(nxd.interpretation)


class TestLegacyNXdata(unittest.TestCase):
    def setUp(self):
        tmp = tempfile.NamedTemporaryFile(
            prefix="nxdata_legacy_examples_", suffix=".h5", delete=True
        )
        tmp.file.close()
        self.h5fname = tmp.name
        self.h5f = h5py.File(tmp.name, "w")

    def tearDown(self):
        self.h5f.close()

    def testSignalAttrOnDataset(self):
        g = self.h5f.create_group("2D")
        g.attrs["NX_class"] = "NXdata"

        ds0 = g.create_dataset("image0", data=numpy.arange(4 * 6).reshape((4, 6)))
        ds0.attrs["signal"] = 1
        ds0.attrs["long_name"] = "My first image"

        ds1 = g.create_dataset("image1", data=numpy.arange(4 * 6).reshape((4, 6)))
        ds1.attrs["signal"] = "2"
        ds1.attrs["long_name"] = "My 2nd image"

        ds2 = g.create_dataset("image2", data=numpy.arange(4 * 6).reshape((4, 6)))
        ds2.attrs["signal"] = 3

        nxd = nxdata.NXdata(self.h5f["2D"])

        self.assertEqual(nxd.signal_dataset_name, "image0")
        self.assertEqual(nxd.signal_name, "My first image")
        self.assertEqual(nxd.signal.shape, (4, 6))

        self.assertEqual(len(nxd.auxiliary_signals), 2)
        self.assertEqual(nxd.auxiliary_signals[1].shape, (4, 6))

        self.assertEqual(nxd.auxiliary_signals_dataset_names, ["image1", "image2"])
        self.assertEqual(nxd.auxiliary_signals_names, ["My 2nd image", "image2"])

    def testAxesOnSignalDataset(self):
        g = self.h5f.create_group("2D")
        g.attrs["NX_class"] = "NXdata"

        ds0 = g.create_dataset("image0", data=numpy.arange(4 * 6).reshape((4, 6)))
        ds0.attrs["signal"] = 1
        ds0.attrs["axes"] = "yaxis:xaxis"

        ds1 = g.create_dataset("yaxis", data=numpy.arange(4))
        ds2 = g.create_dataset("xaxis", data=numpy.arange(6))

        nxd = nxdata.NXdata(self.h5f["2D"])

        self.assertEqual(nxd.axes_dataset_names, ["yaxis", "xaxis"])
        self.assertTrue(numpy.array_equal(nxd.axes[0], numpy.arange(4)))
        self.assertTrue(numpy.array_equal(nxd.axes[1], numpy.arange(6)))

    def testAxesOnAxesDatasets(self):
        g = self.h5f.create_group("2D")
        g.attrs["NX_class"] = "NXdata"

        ds0 = g.create_dataset("image0", data=numpy.arange(4 * 6).reshape((4, 6)))
        ds0.attrs["signal"] = 1
        ds1 = g.create_dataset("yaxis", data=numpy.arange(4))
        ds1.attrs["axis"] = 0
        ds2 = g.create_dataset("xaxis", data=numpy.arange(6))
        ds2.attrs["axis"] = "1"

        nxd = nxdata.NXdata(self.h5f["2D"])
        self.assertEqual(nxd.axes_dataset_names, ["yaxis", "xaxis"])
        self.assertTrue(numpy.array_equal(nxd.axes[0], numpy.arange(4)))
        self.assertTrue(numpy.array_equal(nxd.axes[1], numpy.arange(6)))

    def testAsciiUndefinedAxesAttrs(self):
        """Some files may not be using utf8 for str attrs"""
        g = self.h5f.create_group("bytes_attrs")
        g.attrs["NX_class"] = b"NXdata"
        g.attrs["signal"] = b"image0"
        g.attrs["axes"] = b"yaxis", b"."

        g.create_dataset("image0", data=numpy.arange(4 * 6).reshape((4, 6)))
        g.create_dataset("yaxis", data=numpy.arange(4))

        nxd = nxdata.NXdata(self.h5f["bytes_attrs"])
        self.assertEqual(nxd.axes_dataset_names, ["yaxis", None])


class TestSaveNXdata(unittest.TestCase):
    def setUp(self):
        tmp = tempfile.NamedTemporaryFile(prefix="nxdata", suffix=".h5", delete=True)
        tmp.file.close()
        self.h5fname = tmp.name

    def testSimpleSave(self):
        sig = numpy.array([0, 1, 2])
        a0 = numpy.array([2, 3, 4])
        a1 = numpy.array([3, 4, 5])
        nxdata.save_NXdata(
            filename=self.h5fname,
            signal=sig,
            axes=[a0, a1],
            signal_name="sig",
            axes_names=["a0", "a1"],
            nxentry_name="a",
            nxdata_name="mydata",
        )

        h5f = h5py.File(self.h5fname, "r")
        self.assertTrue(nxdata.is_valid_nxdata(h5f["a/mydata"]))

        nxd = nxdata.NXdata(h5f["/a/mydata"])
        self.assertTrue(numpy.array_equal(nxd.signal, sig))
        self.assertTrue(numpy.array_equal(nxd.axes[0], a0))

        h5f.close()

    def testSimplestSave(self):
        sig = numpy.array([0, 1, 2])
        nxdata.save_NXdata(filename=self.h5fname, signal=sig)

        h5f = h5py.File(self.h5fname, "r")

        self.assertTrue(nxdata.is_valid_nxdata(h5f["/entry/data0"]))

        nxd = nxdata.NXdata(h5f["/entry/data0"])
        self.assertTrue(numpy.array_equal(nxd.signal, sig))
        h5f.close()

    def testSaveDefaultAxesNames(self):
        sig = numpy.array([0, 1, 2])
        a0 = numpy.array([2, 3, 4])
        a1 = numpy.array([3, 4, 5])
        nxdata.save_NXdata(
            filename=self.h5fname,
            signal=sig,
            axes=[a0, a1],
            signal_name="sig",
            axes_names=None,
            axes_long_names=["a", "b"],
            nxentry_name="a",
            nxdata_name="mydata",
        )

        h5f = h5py.File(self.h5fname, "r")
        self.assertTrue(nxdata.is_valid_nxdata(h5f["a/mydata"]))

        nxd = nxdata.NXdata(h5f["/a/mydata"])
        self.assertTrue(numpy.array_equal(nxd.signal, sig))
        self.assertTrue(numpy.array_equal(nxd.axes[0], a0))
        self.assertEqual(nxd.axes_dataset_names, ["dim0", "dim1"])
        self.assertEqual(nxd.axes_names, ["a", "b"])

        h5f.close()

    def testSaveToExistingEntry(self):
        h5f = h5py.File(self.h5fname, "w")
        g = h5f.create_group("myentry")
        g.attrs["NX_class"] = "NXentry"
        h5f.close()

        sig = numpy.array([0, 1, 2])
        a0 = numpy.array([2, 3, 4])
        a1 = numpy.array([3, 4, 5])
        nxdata.save_NXdata(
            filename=self.h5fname,
            signal=sig,
            axes=[a0, a1],
            signal_name="sig",
            axes_names=["a0", "a1"],
            nxentry_name="myentry",
            nxdata_name="toto",
        )

        h5f = h5py.File(self.h5fname, "r")
        self.assertTrue(nxdata.is_valid_nxdata(h5f["myentry/toto"]))

        nxd = nxdata.NXdata(h5f["myentry/toto"])
        self.assertTrue(numpy.array_equal(nxd.signal, sig))
        self.assertTrue(numpy.array_equal(nxd.axes[0], a0))
        h5f.close()


class TestGetDefault:
    """Test silx.io.nxdata.get_default function"""

    @pytest.fixture
    def hdf5_file(self, tmp_path):
        with h5py.File(tmp_path / "test_file.h5", "w") as h5f:
            yield h5f

    def testDirectPath(self, hdf5_file):
        dicttoh5(
            {
                ("", "default"): "/nxentry/nxprocess/nxdata",
                "nxentry": {
                    "nxprocess": {
                        "nxdata": {
                            ("", "NX_class"): "NXdata",
                            ("", "signal"): "data",
                            "data": (1, 2, 3),
                        }
                    }
                },
            },
            hdf5_file,
        )
        default = nxdata.get_default(hdf5_file)
        assert isinstance(default, nxdata.NXdata)
        assert default.group.name == "/nxentry/nxprocess/nxdata"

    def testAbsolutePath(self, hdf5_file):
        dicttoh5(
            {
                ("", "default"): "/nxentry",
                "nxentry": {
                    ("", "default"): "/nxentry/nxprocess/nxdata",
                    "nxprocess": {
                        "nxdata": {
                            ("", "NX_class"): "NXdata",
                            ("", "signal"): "data",
                            "data": (1, 2, 3),
                        }
                    },
                },
            },
            hdf5_file,
        )
        default = nxdata.get_default(hdf5_file)
        assert isinstance(default, nxdata.NXdata)
        assert default.group.name == "/nxentry/nxprocess/nxdata"

    def testRelativePath(self, hdf5_file):
        dicttoh5(
            {
                ("", "default"): "nxentry",
                "nxentry": {
                    ("", "default"): "nxdata",
                    "nxdata": {
                        ("", "NX_class"): "NXdata",
                        ("", "signal"): "data",
                        "data": (1, 2, 3),
                    },
                },
            },
            hdf5_file,
        )
        default = nxdata.get_default(hdf5_file)
        assert isinstance(default, nxdata.NXdata)
        assert default.group.name == "/nxentry/nxdata"

    def testRelativePathSubdir(self, hdf5_file):
        dicttoh5(
            {
                ("", "default"): "nxentry",
                "nxentry": {
                    ("", "default"): "nxprocess/nxdata",
                    "nxprocess": {
                        "nxdata": {
                            ("", "NX_class"): "NXdata",
                            ("", "signal"): "data",
                            "data": (1, 2, 3),
                        }
                    },
                },
            },
            hdf5_file,
        )
        default = nxdata.get_default(hdf5_file)
        assert isinstance(default, nxdata.NXdata)
        assert default.group.name == "/nxentry/nxprocess/nxdata"

    def testRecursiveAbsolutePath(self, hdf5_file):
        dicttoh5(
            {
                ("", "default"): "/nxentry",
                "nxentry": {
                    ("", "default"): "/nxentry/nxprocess",
                    "nxprocess": {
                        ("", "default"): "/nxentry/nxprocess/nxdata",
                        "nxdata": {
                            ("", "NX_class"): "NXdata",
                            ("", "signal"): "data",
                            "data": (1, 2, 3),
                        },
                    },
                },
            },
            hdf5_file,
        )
        default = nxdata.get_default(hdf5_file)
        assert isinstance(default, nxdata.NXdata)
        assert default.group.name == "/nxentry/nxprocess/nxdata"

    def testRecursiveRelativePath(self, hdf5_file):
        dicttoh5(
            {
                ("", "default"): "nxentry",
                "nxentry": {
                    ("", "default"): "nxprocess",
                    "nxprocess": {
                        ("", "default"): "nxdata",
                        "nxdata": {
                            ("", "NX_class"): "NXdata",
                            ("", "signal"): "data",
                            "data": (1, 2, 3),
                        },
                    },
                },
            },
            hdf5_file,
        )
        default = nxdata.get_default(hdf5_file)
        assert isinstance(default, nxdata.NXdata)
        assert default.group.name == "/nxentry/nxprocess/nxdata"

    def testLoop(self, hdf5_file):
        """Infinite loop of @default"""
        dicttoh5(
            {
                ("", "default"): "/nxentry",
                "nxentry": {
                    ("", "default"): "/nxentry",
                },
            },
            hdf5_file,
        )
        default = nxdata.get_default(hdf5_file)
        assert default is None
