diff --git a/.travis.yml b/.travis.yml
index 719c104..39ada59 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -39,5 +39,5 @@ after_success:
   - coverage report -m
   - which coveralls
   - coveralls
-  - source deactivate
+  - conda deactivate
   - conda env remove -y -n $PY_ENV
diff --git a/src/spec2nexus/_requirements.py b/src/spec2nexus/_requirements.py
index 657df11..8f3e57a 100644
--- a/src/spec2nexus/_requirements.py
+++ b/src/spec2nexus/_requirements.py
@@ -8,7 +8,7 @@
 # The full license is in the file LICENSE.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 
-
+REQUIREMENTS = "requirements.txt"
 
 def learn_requirements():
     """
@@ -16,12 +16,10 @@ def learn_requirements():
     
     ALL packages & version restrictions stated in requirements.txt
     """
-    req_file = 'requirements.txt'
-    reqs = []
-
     import os
     path = os.path.dirname(__file__)
-    req_file = os.path.join(path, '..', req_file)
+    req_file = os.path.join(path, '..', '..', REQUIREMENTS)
+    reqs = []
     if not os.path.exists(req_file):
         # not needed with installed package
         return reqs
diff --git a/src/spec2nexus/writer.py b/src/spec2nexus/writer.py
index 5bd6b50..911df40 100644
--- a/src/spec2nexus/writer.py
+++ b/src/spec2nexus/writer.py
@@ -181,24 +181,30 @@ class Writer(object):
             signal, axes = self.oneD(nxdata, scan)
 
         # these locations suggested to NIAC, easier to parse than attached to dataset!
-        if len(signal) == 0:
-            pass
+        # if len(signal) == 0:
+        #     pass
         
         # Syntax of axes attribute (http://wiki.nexusformat.org/2014_axes_and_uncertainties):
         #  @axes="H:K"       INCOREECT
         #  @axes="H", "K"    CORRECT
         if axes.find(':') >= 0:
-            axes = axes.split(':')
+            def fixer(s):
+                # h5py requires list of strings to be encoded
+                # see: https://stackoverflow.com/questions/23220513/storing-a-list-of-strings-to-a-hdf5-dataset-from-python
+                return s.encode("ascii", "ignore")
+            axes = list(map(fixer, axes.split(":")))
         eznx.addAttributes(nxdata, signal=signal, axes=axes)
-        #eznx.addAttributes(nxdata[signal], signal=1, axes=axes)    # deprecated now
-        # assume here that "axis_name" has rank=1
-        # TODO: test that scan.data[axis_name] has rank=1
         indices = [0,]     # 0-based reference
         if isinstance(axes, str):
             eznx.addAttributes(nxdata, **{axes+'_indices': indices})
         else:
             for axis_name in axes:
-                eznx.addAttributes(nxdata, **{axis_name+'_indices': indices})
+                axis_name = axis_name.decode("utf-8") 
+                # assume here that "axis_name" has rank=1
+                # if scan.data[axis_name] != 1:
+                #     pass    # TODO: and do what?
+                k = "%s%s" % (axis_name, '_indices')
+                eznx.addAttributes(nxdata, **{k: indices})
     
     def oneD(self, nxdata, scan):
         """*internal*: generic data parser for 1-D column data, returns signal and axis"""
@@ -284,8 +290,8 @@ class Writer(object):
                 if label not in nxdata:
                     axis = np.array( scan.data.get(label) )
                     self.write_ds(nxdata, label, utils.reshape_data(axis, data_shape))
-                else:
-                    pass
+                # else:
+                #     pass
 
             signal = utils.clean_name(scan.column_last)
             axes = ':'.join([label1, label2])

writer: expand unit tests
More code could be tested in the writer module.  See: https://coveralls.io/builds/23443404/source?filename=src/spec2nexus/writer.py
diff --git a/tests/test_requirements.py b/tests/test_requirements.py
new file mode 100644
index 0000000..2a1cc90
--- /dev/null
+++ b/tests/test_requirements.py
@@ -0,0 +1,54 @@
+'''
+unit tests for the _requirements module
+'''
+
+#-----------------------------------------------------------------------------
+# :author:    Pete R. Jemian
+# :email:     prjemian@gmail.com
+# :copyright: (c) 2014-2019, Pete R. Jemian
+#
+# Distributed under the terms of the Creative Commons Attribution 4.0 International Public License.
+#
+# The full license is in the file LICENSE.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+import os, sys
+import numpy
+import unittest
+from builtins import isinstance
+
+_test_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+_path = os.path.abspath(os.path.join(_test_path, 'src'))
+
+sys.path.insert(0, _path)
+sys.path.insert(0, _test_path)
+
+from spec2nexus import _requirements
+
+import tests.common
+
+
+class Test(unittest.TestCase):
+
+    def test_learn_requirements(self):
+        reqs = _requirements.learn_requirements()
+        self.assertTrue(isinstance(reqs, list))
+        self.assertGreater(len(reqs), 0)
+        self.assertEqual(len(reqs), 5)
+        self.assertTrue("h5py" in reqs)
+        self.assertTrue("numpy" in reqs)
+
+
+def suite(*args, **kw):
+    test_suite = unittest.TestSuite()
+    test_list = [
+        Test,
+        ]
+    for test_case in test_list:
+        test_suite.addTest(unittest.makeSuite(test_case))
+    return test_suite
+
+
+if __name__ == "__main__":
+    runner=unittest.TextTestRunner()
+    runner.run(suite())
diff --git a/tests/test_writer.py b/tests/test_writer.py
index 133963e..07ed492 100644
--- a/tests/test_writer.py
+++ b/tests/test_writer.py
@@ -12,7 +12,11 @@ unit tests for the writer module
 # The full license is in the file LICENSE.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 
-import os, sys
+import h5py
+import os
+import shutil
+import sys
+import tempfile
 import unittest
 
 _test_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
@@ -48,20 +52,99 @@ class TestWriter(unittest.TestCase):
         out = writer.Writer(spec_data)
         scan_list = [1, 5, 7]
         out.save(self.hname, scan_list)
-        # TODO: make tests of other things in the Writer
+
         dd = out.root_attributes()
         self.assertTrue(isinstance(dd, dict))
+        
+        # TODO: test writer's various functions and methods
+
+        # test file written by Writer
+        with h5py.File(self.hname, "r") as hp:
+            root = hp["/"]
+            default = root.attrs.get("default")
+            self.assertNotEqual(default, None)
+            self.assertTrue(default in root)
+            nxentry = root[default]
+
+            default = nxentry.attrs.get("default")
+            self.assertNotEqual(default, None)
+            self.assertTrue(default in nxentry)
+            nxdata = nxentry[default]
+
+            signal = nxdata.attrs.get("signal")
+            self.assertNotEqual(signal, None)
+            self.assertTrue(signal in nxdata)
+
+
+class TestMeshes(unittest.TestCase):
+
+    def setUp(self):
+        self._owd = os.getcwd()
+        self.tempdir = tempfile.mkdtemp()
+        os.chdir(self.tempdir)
+
+    def tearDown(self):
+        if os.path.exists(self._owd):
+            os.chdir(self._owd)
+        if os.path.exists(self.tempdir):
+            shutil.rmtree(self.tempdir, ignore_errors=True)
+
+    def test_save_data_mesh(self):
+        #S 22  mesh  eta 57 57.1 10  chi 90.9 91 10  1
+        fname = os.path.join(_path, "spec2nexus", 'data', '33id_spec.dat')
+        hname = "test.h5"
+        spec_data = spec.SpecDataFile(fname)
+        out = writer.Writer(spec_data)
+        out.save(hname, [22])
+  
+        with h5py.File(hname, "r") as hp:
+            root = hp["/"]
+            nxdata = root["/S22/data"]
+            signal = nxdata.attrs["signal"]
+            self.assertEqual(nxdata[signal][()].shape, (11, 11))
+
+            ds = nxdata["_mca_"]
+            self.assertEqual(ds[()].shape, (11, 11, 91))
+            self.assertEqual(ds.attrs["axes"], "eta:chi:_mca_channel_")
+            self.assertEqual(ds.attrs["spec_name"], "_mca_")
+            self.assertEqual(ds.attrs["units"], "counts")
+
+    def test_save_data_hklmesh(self):
+        #S 17  hklmesh  H 1.9 2.1 100  K 1.9 2.1 100  -800000
+        fname = os.path.join(_path, "spec2nexus", 'data', '33bm_spec.dat')
+        hname = "test.h5"
+        spec_data = spec.SpecDataFile(fname)
+        out = writer.Writer(spec_data)
+        out.save(hname, [17])
+  
+        with h5py.File(hname, "r") as hp:
+            root = hp["/"]
+            nxdata = root["/S17/data"]
+            signal = nxdata.attrs["signal"]
+            axes = nxdata.attrs["axes"]
+            self.assertEqual(axes[0], b"H")
+            self.assertEqual(axes[1], b"K")
+
+    def test_save_data_hklscan(self):
+        #S 104 hklscan -0.0500048 -0.0500048 0.05 0.2 12.0002 12.0002 30 2
+        fname = os.path.join(_path, "spec2nexus", 'data', '33id_spec.dat')
+        hname = "test.h5"
+        spec_data = spec.SpecDataFile(fname)
+        out = writer.Writer(spec_data)
 
-#     sys.argv.append(os.path.join('data', 'APS_spec_data.dat'))
-#     sys.argv.append(os.path.join('data', '33id_spec.dat'))
-#     sys.argv.append(os.path.join('data', '33bm_spec.dat'))
-#     sys.argv.append(os.path.join('data', 'lmn40.spe'))
+        with self.assertRaises(NotImplementedError) as context:
+            out.save(hname, [104])
+        received = str(context.exception)
+        expected = "hklscan save_data() not yet implemented"
+        self.assertTrue(received.startswith(expected))
+        # FIXME:
 
 
 def suite(*args, **kw):
     test_suite = unittest.TestSuite()
     test_list = [
         TestWriter,
+        TestMeshes,
         ]
     for test_case in test_list:
         test_suite.addTest(unittest.makeSuite(test_case))

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest versioneer coverage coveralls
apt-get update apt-get install -y gcc
3.7
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi @ file:///croot/certifi_1671487769961/work/certifi
charset-normalizer==3.4.1
coverage==6.5.0
coveralls==3.3.1
cycler==0.11.0
docopt==0.6.2
exceptiongroup==1.2.2
fonttools==4.38.0
h5py==3.8.0
idna==3.10
importlib-metadata==6.7.0
iniconfig==2.0.0
kiwisolver==1.4.5
lxml==5.3.1
matplotlib==3.5.3
numpy==1.21.6
packaging==24.0
Pillow==9.5.0
pluggy==1.2.0
pyparsing==3.1.4
pytest==7.4.4
python-dateutil==2.9.0.post0
requests==2.31.0
six==1.17.0
-e git+https://github.com/prjemian/spec2nexus.git@ff11c0f1d0a5a26e28ae194208d4da7b902ef872#egg=spec2nexus
tomli==2.0.1
typing_extensions==4.7.1
urllib3==2.0.7
versioneer==0.29
zipp==3.15.0

name: spec2nexus
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - charset-normalizer==3.4.1
      - coverage==6.5.0
      - coveralls==3.3.1
      - cycler==0.11.0
      - docopt==0.6.2
      - exceptiongroup==1.2.2
      - fonttools==4.38.0
      - h5py==3.8.0
      - idna==3.10
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - kiwisolver==1.4.5
      - lxml==5.3.1
      - matplotlib==3.5.3
      - numpy==1.21.6
      - packaging==24.0
      - pillow==9.5.0
      - pluggy==1.2.0
      - pyparsing==3.1.4
      - pytest==7.4.4
      - python-dateutil==2.9.0.post0
      - requests==2.31.0
      - six==1.17.0
      - tomli==2.0.1
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - versioneer==0.29
      - zipp==3.15.0
prefix: /opt/conda/envs/spec2nexus

tests/test_requirements.py::Test::test_learn_requirements
tests/test_writer.py::TestWriter::testWriter tests/test_writer.py::TestMeshes::test_save_data_hklmesh tests/test_writer.py::TestMeshes::test_save_data_hklscan tests/test_writer.py::TestMeshes::test_save_data_mesh
src/spec2nexus/_requirements.py src/spec2nexus/writer.py .travis.yml
src/spec2nexus/_requirements.py src/spec2nexus/writer.py .travis.yml