diff --git a/.travis.yml b/.travis.yml
index 48cd9ff..57396dd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,11 +6,13 @@
 #  https://docs.travis-ci.com/user/customizing-the-build#The-Build-Lifecycle
 #  https://docs.travis-ci.com/user/languages/python
 
+dist: xenial   # required for Python >= 3.7
 language: python
 python:
   - "2.7"
   - "3.5"
   - "3.6"
+  - "3.7"
 
 before_script:
   - wget http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh
diff --git a/CHANGES.rst b/CHANGES.rst
index 34a9c7e..7a2cb27 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -7,6 +7,15 @@ Change History
 Production
 **********
 
+:2020.0.0: *tba*, release
+
+    * `#145 <https://github.com/prjemian/spec2nexus/issues/145>`_
+       test more content from headers
+    * `#144 <https://github.com/prjemian/spec2nexus/issues/144>`_
+       eznx `makeDataset()` now recognizes if data is `ndarray`
+    * `#123 <https://github.com/prjemian/spec2nexus/issues/123>`_
+       Accept data files with no header control lines (#F #E #D #C sequence)
+
 :2019.0503.0: 2019.05.03, tag
 
     * `#142 <https://github.com/prjemian/spec2nexus/issues/142>`_
diff --git a/src/spec2nexus/charts.py b/src/spec2nexus/charts.py
index eead9e7..e3621e5 100755
--- a/src/spec2nexus/charts.py
+++ b/src/spec2nexus/charts.py
@@ -13,6 +13,7 @@ charting for spec2nexus
 import datetime
 import numpy
 import matplotlib
+import matplotlib.figure
 matplotlib.use('Agg')
 from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
 
diff --git a/src/spec2nexus/plugin.py b/src/spec2nexus/plugin.py
index d26ff3e..4d4eb6d 100644
--- a/src/spec2nexus/plugin.py
+++ b/src/spec2nexus/plugin.py
@@ -220,9 +220,18 @@ class PluginManager(object):
         if pos < 0:
             return None
         text = spec_data_file_line[:pos]
+        
+        # try to locate the key directly
+        handler = self.handler_dict.get(text)
+        if handler is not None:
+            return text
+        
+        # search and match using regular expressions
         for key, handler in self.handler_dict.items():
             if handler().match_key(text):
                 return key
+        
+        # give up
         return None
 
     def get(self, key):
diff --git a/src/spec2nexus/plugins/spec_common_spec2nexus.py b/src/spec2nexus/plugins/spec_common_spec2nexus.py
index 8672939..fc3aa67 100644
--- a/src/spec2nexus/plugins/spec_common_spec2nexus.py
+++ b/src/spec2nexus/plugins/spec_common_spec2nexus.py
@@ -19,6 +19,8 @@ SPEC data file standard control lines
 
 """
 
+from collections import OrderedDict
+import datetime
 import re
 
 from spec2nexus.eznx import write_dataset, makeGroup, openGroup
@@ -53,7 +55,10 @@ class SPEC_File(ControlLineHandler):
     key = '#F'
     
     def process(self, text, spec_file_obj, *args, **kws):
-        spec_file_obj.specFile = strip_first_word(text)
+        if not hasattr(spec_file_obj, "specFile"):
+            spec_file_obj.specFile = None
+        if spec_file_obj.specFile in (None, ""):
+            spec_file_obj.specFile = strip_first_word(text)
 
 
 class SPEC_Epoch(ControlLineHandler):
@@ -75,15 +80,15 @@ class SPEC_Epoch(ControlLineHandler):
 
     key = '#E'
     
-    def process(self, buf, scan, *args, **kws):
-        header = SpecDataFileHeader(buf, parent=scan)
+    def process(self, buf, sdf_object, *args, **kws):
+        header = SpecDataFileHeader(buf, parent=sdf_object)
         line = buf.splitlines()[0].strip()
         if line.find(".") > -1:
             header.epoch = float(strip_first_word(line))
         else:
             header.epoch = int(strip_first_word(line))
+        sdf_object.headers.append(header)
         header.interpret()                  # parse the full header
-        scan.headers.append(header)
 
 
 class SPEC_Date(ControlLineHandler):
@@ -102,14 +107,22 @@ class SPEC_Date(ControlLineHandler):
 
     key = '#D'
     
-    def process(self, text, scan, *args, **kws):
-        scan.date = strip_first_word(text)
-        if isinstance(scan, SpecDataFileScan):
-            scan.addH5writer(self.key, self.writer)
+    def process(self, text, sdf_object, *args, **kws):
+        text = strip_first_word(text)
+        # if isinstance(sdf_object, SpecDataFileScan):
+        #     sdf_object = sdf_object.header
+        sp = text.split(" ")
+        if len(sp) == 1:
+            sdf_object.epoch = int(float(text))
+            text = datetime.datetime.fromtimestamp(float(text))
+            text = text.strftime("%c")
+        sdf_object.date = text
+        if isinstance(sdf_object, SpecDataFileScan):
+            sdf_object.addH5writer(self.key, self.writer)
     
-    def writer(self, h5parent, writer, scan, *args, **kws):
+    def writer(self, h5parent, writer, sdf_object, *args, **kws):
         """Describe how to store this data in an HDF5 NeXus file"""
-        write_dataset(h5parent, "date", iso8601(scan.date)  )
+        write_dataset(h5parent, "date", iso8601(sdf_object.date)  )
 
 
 class SPEC_Comment(ControlLineHandler):
@@ -179,13 +192,21 @@ class SPEC_Scan(ControlLineHandler):
 
     key = '#S'
     
-    def process(self, part, spec_obj, *args, **kws):
-        scan = SpecDataFileScan(spec_obj.headers[-1], part, parent=spec_obj)
-        text = part.splitlines()[0].strip()
-        scan.S = strip_first_word(text)
+    def process(self, part, sdf, *args, **kws):
+        if len(sdf.headers) == 0:
+            # make a header if none exists now
+            raw = ""        # TODO: what default content to use?
+            header = SpecDataFileHeader(raw, parent=sdf)
+            sdf.headers.append(header)
+        else:
+            header = sdf.headers[-1]    # pick the most recent header
+
+        scan = SpecDataFileScan(header, part, parent=sdf)
+        scan.S = strip_first_word(part.splitlines()[0].strip())
         scan.scanNum = scan.S.split()[0]
         scan.scanCmd = strip_first_word(scan.S)
-        if scan.scanNum in spec_obj.scans:
+        
+        if scan.scanNum in sdf.scans:
             # Before raising an exception, 
             #    Check for duplicate and create alternate name
             #    write as "%d.%d" % (scan.scanNum, index+1) 
@@ -195,14 +216,14 @@ class SPEC_Scan(ControlLineHandler):
             # Will a non-integer scanNum break anything?  [note: It *has* caused troubles.]
             for i in range(len(scan.parent.scans)):
                 new_scanNum = "%s.%d" % (scan.scanNum, i+1)
-                if new_scanNum not in spec_obj.scans:
+                if new_scanNum not in sdf.scans:
                     scan.scanNum = new_scanNum
                     break
         scan.scanNum = str(scan.scanNum)
-        if scan.scanNum in spec_obj.scans:
-            msg = str(scan.scanNum) + ' in ' + spec_obj.fileName
+        if scan.scanNum in sdf.scans:
+            msg = str(scan.scanNum) + ' in ' + sdf.fileName
             raise DuplicateSpecScanNumber(msg)
-        spec_obj.scans[scan.scanNum] = scan
+        sdf.scans[scan.scanNum] = scan
 
 
 class SPEC_Geometry(ControlLineHandler):
@@ -482,8 +503,13 @@ class SPEC_PositionerNames(ControlLineHandler):
 
     key = '#O\d+'
     
-    def process(self, text, header, *args, **kws):
-        header.O.append(split_column_labels(strip_first_word(text)))
+    def process(self, text, sdf_object, *args, **kws):
+        if isinstance(sdf_object, SpecDataFileScan):
+            sdf_object = sdf_object.header
+        key = text.split()[0]
+        if key == "#O0":
+            sdf_object.O = []       # TODO: What if motor names are different?
+        sdf_object.O.append(split_column_labels(strip_first_word(text)))
 
 
 class SPEC_PositionerMnemonics(ControlLineHandler):
@@ -561,6 +587,8 @@ class SPEC_Positioners(ControlLineHandler):
     key = '#P\d+'
     
     def process(self, text, scan, *args, **kws):
+        if isinstance(scan, SpecDataFileHeader):
+            scan = scan.getLatestScan()
         scan.P.append( strip_first_word(text) )
         scan.addPostProcessor('motor_positions', self.postprocess)
     
@@ -570,7 +598,7 @@ class SPEC_Positioners(ControlLineHandler):
         
         :param SpecDataFileScan scan: data from a single SPEC scan
         """
-        scan.positioner = {}
+        scan.positioner = OrderedDict()
         for row, values in enumerate(scan.P):
             if row >= len(scan.header.O):
                 scan.add_interpreter_comment('#P%d found without #O%d' % (row, row))
diff --git a/src/spec2nexus/spec.py b/src/spec2nexus/spec.py
index 41de9cc..b495a5a 100644
--- a/src/spec2nexus/spec.py
+++ b/src/spec2nexus/spec.py
@@ -113,9 +113,10 @@ Try to read a file that does not exist:
 
 """
 
-
-import re       #@UnusedImport
+from collections import OrderedDict
+import datetime
 import os       #@UnusedImport
+import re       #@UnusedImport
 import sys      #@UnusedImport
 import time
 from spec2nexus.utils import get_all_plugins
@@ -148,6 +149,26 @@ def is_spec_file(filename):
     
     :param str filename: path/to/possible/spec/data.file
 
+    *filename* is a SPEC file if it contains at least one #S control line
+    """
+    if not os.path.exists(filename) or not os.path.isfile(filename):
+        return False
+    try:
+        with open(filename, "r") as fp:
+            for line in fp.readlines():
+                if line.startswith("#S "):
+                    return True
+    except Exception:
+        pass
+    return False
+
+
+def is_spec_file_with_header(filename):
+    """
+    test if a given file name is a SPEC data file
+    
+    :param str filename: path/to/possible/spec/data.file
+
     *filename* is a SPEC file only if the file starts [#]_ 
     with these control lines in order:
 
@@ -199,7 +220,7 @@ class SpecDataFile(object):
         global plugin_manager
         self.fileName = None
         self.headers = []
-        self.scans = {}
+        self.scans = OrderedDict()
         self.readOK = -1
         if not os.path.exists(filename):
             raise SpecDataFileNotFound('file does not exist: ' + str(filename))
@@ -216,51 +237,37 @@ class SpecDataFile(object):
     def __str__(self):
         return self.fileName or 'None'
 
-    def read(self):
-        """Reads and parses a spec data file"""
-        buf = self._read_file_(self.fileName)
+    def get_sections(self, buf):
+        """
+        divide (SPEC data file text) buffer into sections
         
-        text = buf.splitlines()[0].strip()
-        key = self.plugin_manager.getKey(text)
-        if key != '#F':
-            raise NotASpecDataFile('First line does not start with #F: ' + self.fileName)
-        self.plugin_manager.process(key, text, self)
-        buf = buf[len(text):]
-
-        #------------------------------------------------------
-        # identify all header blocks: split buf on #E control lines
-        headers = []                            # caution: some files may have EOL = \r\n
-        for part in buf.split('\n#E '):         # Identify the spec file header sections (usually just 1)
-            if len(part.strip()) == 0: continue         # just in case DOS EOL
-            key = self.plugin_manager.getKey(part.splitlines()[0].strip())
-            if key != '#E':
-                headers.append('#E ' + part)        # new header is starting
-        del buf                                 # Dispose of the input buffer memory (necessary?)
-        # headers is a list of the #E header blocks (contains all #S scan blocks)
-
-        #------------------------------------------------------
-        # walk through all header blocks: split each header block on #S control lines
-        self.parts = []         # break into list of blocks, either #E or #S, in order of appearance
-        for part in headers:
-            for block in part.split('\n#S '):   # break header sections by scans
-                text = block.splitlines()[0].strip()
-                if self.plugin_manager.getKey(text) not in ('#E', '#S'):
-                    block = '#S ' + block
-                self.parts.append(block)        # keep each block (starts with either #E or #S)
-        del headers, block
-        # self.parts is a list of the #E and #S parts
-
-        #------------------------------------------------------
-        # pull the information from each scan head
-        for part in self.parts:
-            text = part.splitlines()[0].strip()
-            key = self.plugin_manager.getKey(text)
-            if key in ('#E', '#S'):
-                self.plugin_manager.process(key, part, self)
-            else:
-                msg = part.splitlines()[0].strip()
-                msg += " in " + self.fileName
-                raise UnknownSpecFilePart(msg)
+        internal: A *block* starts with either #F | #E | #S
+        
+        PARAMETERS
+        
+        buf : str
+            the contents of the SPEC data file
+        
+        RETURNS
+        
+        sections : [[str]]
+            list of blocks where each block is the text from `buf`
+        
+        """
+        sections, block = [], []
+        
+        for _line_num, text in enumerate(buf.splitlines()):
+            if len(text.strip()) > 0:
+                f = text.split()[0]
+                if len(f) == 2 and f in ("#E", "#F", "#S"):
+                    if len(block) > 0:
+                        sections.append("\n".join(block))
+                    block = []
+            block.append(text)
+
+        if len(block) > 0:
+            sections.append("\n".join(block))
+        return sections
 
     def _read_file_(self, spec_file_name):
         """Reads a spec data file"""
@@ -277,6 +284,26 @@ class SpecDataFile(object):
         # convert all '\r\n' to '\n', then all '\r' to '\n'
 
         return buf.replace('\r\n', '\n').replace('\r', '\n')
+
+    def read(self):
+        """Reads and parses a spec data file"""
+        buf = self._read_file_(self.fileName)
+        
+        sections = self.get_sections(buf)
+        for block in sections:
+            if len(block) == 0:
+                continue
+            key = self.plugin_manager.getKey(block.splitlines()[0])
+            self.plugin_manager.process(key, block, self)
+            
+            if key == "#S":
+                scan = list(self.scans.values())[-1]
+                for line in scan.raw.splitlines()[1:]:
+                    if len(line) > 0:
+                        key = line.split()[0]
+                        if key in ("#D",):
+                            self.plugin_manager.process(key, line, scan)
+                            break
     
     def getScan(self, scan_number=0):
         """return the scan number indicated, None if not found"""
@@ -337,7 +364,7 @@ class SpecDataFile(object):
 
 
 class SpecDataFileHeader(object):
-    """contents of a spec data file header (#F) section"""
+    """contents of a spec data file header (#E) section"""
 
     def __init__(self, buf, parent = None):
         #----------- initialize the instance variables
@@ -400,12 +427,15 @@ class SpecDataFileHeader(object):
         """
         if label not in self.h5writers:
             self.h5writers[label] = func
+    
+    def getLatestScan(self):
+        return list(self.parent.scans.values())[-1]
 
 
 #-------------------------------------------------------------------------------------------
 
 LAZY_INTERPRET_SCAN_DATA_ATTRIBUTES = [
-    'comments', 'data', 'data_lines', 'date', 'G', 'I',
+    'comments', 'data', 'data_lines', 'G', 'I',
     'L', 'M', 'positioner', 'N', 'P', 'Q', 'T', 'U',
     'column_first', 'column_last',
 ]
@@ -435,7 +465,10 @@ class SpecDataFileScan(object):
         self._interpreter_comments_ = []
         if parent is not None:
             # avoid changing the interface for clients
-            self.specFile = parent.fileName
+            if isinstance(parent, SpecDataFile):
+                self.specFile = parent.fileName
+            elif isinstance(parent, SpecDataFileHeader):
+                self.specFile = parent.parent.fileName
         elif self.header is not None:
             if self.header.parent is not None:
                 self.specFile = self.header.parent.fileName

accept SPEC data files that do not have header section
Per issues #122 and https://github.com/sardana-org/sardana/issues/735#issuecomment-382312675,  it is reasonable to change the criteria for what content defines a text file as a SPEC data file.

A minimum qualification is that the file must have at least one `#S` control line.
diff --git a/tests/__init__.py b/tests/__init__.py
index 66e373d..12f03dd 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -39,6 +39,7 @@ def suite(*args, **kw):
     from tests import issue99_hklscan
     from tests import issue107
     from tests import issue119
+    from tests import issue123
     test_suite = unittest.TestSuite()
     test_list = [
         data_03_06_JanTest,
@@ -55,6 +56,8 @@ def suite(*args, **kw):
         issue64,
         issue99_hklscan,
         issue107,
+        issue119,
+        issue123,
         ]
 
     for test in test_list:
diff --git a/tests/issue107.py b/tests/issue107.py
index 549529a..1aa68df 100644
--- a/tests/issue107.py
+++ b/tests/issue107.py
@@ -51,6 +51,7 @@ class Issue107(unittest.TestCase):
         scanNum = 4
         scan = specData.getScan(scanNum)
         self.assertTrue(hasattr(scan.header, "H"))
+        self.assertTrue(hasattr(scan, "V"))
         scan.interpret()
         self.assertTrue(hasattr(scan, "metadata"))
         self.assertEqual(len(scan.metadata), 140)
diff --git a/tests/issue123.py b/tests/issue123.py
new file mode 100644
index 0000000..c09ed06
--- /dev/null
+++ b/tests/issue123.py
@@ -0,0 +1,123 @@
+
+'''
+test issue 123
+'''
+
+#-----------------------------------------------------------------------------
+# :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
+import sys
+import unittest
+
+_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'src'))
+if _path not in sys.path:
+    sys.path.insert(0, _path)
+
+from spec2nexus import spec
+
+_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+if _path not in sys.path:
+    sys.path.insert(0, _path)
+import tests.common
+
+
+class Issue123(unittest.TestCase):
+   
+    def setUp(self):
+        self.path = os.path.dirname(spec.__file__)
+        self.sys_argv0 = sys.argv[0]
+
+    def tearDown(self):
+        sys.argv = [self.sys_argv0,]
+
+    def test_spock_file(self):
+        testfile = os.path.join(self.path, 'data', 'spec_from_spock.spc')
+
+        self.assertTrue(os.path.exists(testfile))
+
+        self.assertFalse(spec.is_spec_file_with_header(testfile))
+        self.assertTrue(spec.is_spec_file(testfile))
+
+        specData = spec.SpecDataFile(testfile)
+        self.assertTrue(isinstance(specData, spec.SpecDataFile))
+        
+        self.assertEqual(len(specData.headers), 1, "expected number of headers")
+        header = specData.headers[0]
+        self.assertTrue(isinstance(header, spec.SpecDataFileHeader))
+        self.assertEqual(len(header.raw), 0, "no raw content")
+        self.assertEqual(len(header.date), 0, "default date")
+        self.assertEqual(header.epoch, 0, "default epoch")
+        self.assertEqual(len(header.comments), 0, "expected number of header comments")
+        self.assertEqual(len(header.O), 0, "defined positioner label rows")
+        self.assertEqual(len(header.H), 0, "defined metadata label rows")
+
+        scans = specData.getScanNumbers()
+        self.assertEqual(len(scans), 171, "expected number of scans")
+        scan = specData.getScan(2)
+        self.assertTrue(isinstance(scan, spec.SpecDataFileScan))
+
+        self.assertEqual(len(scan.P), 20, "defined positioner value rows")
+        self.assertEqual(len(header.O), 20, "defined positioner label rows")
+        self.assertEqual(scan.header.O, header.O, "same object")
+        self.assertEqual(len(scan.V), 0, "defined metadata value rows")
+        self.assertEqual(len(scan.L), 23, "defined data column labels")
+        self.assertEqual(len(scan.data), 23, "defined data variables")
+        self.assertEqual(scan.L[0], "Pt_No", "first data column label")
+        self.assertEqual(scan.L[-1], "dt", "last data column label")
+        d1 = scan.data.get("Pt_No")
+        self.assertNotEqual(d1, None, "data 'Pt_No' exists")
+        self.assertEqual(len(d1), 51, "data 'Pt_No' has expected number of values")
+        self.assertEqual(scan.scanCmd, "dscan th -0.5 0.5 50 1.0", "scan command")
+        self.assertEqual(scan.scanNum, "2", "scan number as string")
+        self.assertNotEqual(scan.scanNum, 2, "scan number as integer")
+        self.assertEqual(len(scan.positioner), 155, "defined positioners")
+        d1 = scan.positioner.get("abs")
+        self.assertNotEqual(d1, None, "positioner 'abs' exists")
+        self.assertEqual(d1, 0.0, "positioner 'abs' value is zero")
+
+    def test_33id_file(self):
+        testfile = os.path.join(self.path, 'data', '33id_spec.dat')
+
+        self.assertTrue(os.path.exists(testfile))
+
+        self.assertTrue(spec.is_spec_file_with_header(testfile))
+        self.assertTrue(spec.is_spec_file(testfile))
+
+        specData = spec.SpecDataFile(testfile)
+        self.assertTrue(isinstance(specData, spec.SpecDataFile))
+
+        scans = specData.getScanNumbers()
+        self.assertEqual(len(scans), 106, "expected number of scans")
+        
+        self.assertEqual(len(specData.headers), 1, "expected number of headers")
+        header = specData.headers[0]
+        self.assertTrue(isinstance(header, spec.SpecDataFileHeader))
+        self.assertGreater(len(header.raw), 0, "raw content")
+        self.assertGreater(len(header.date), 0, "defined date")
+        self.assertEqual(header.epoch, 1058427452, "expected epoch")
+        self.assertEqual(len(header.comments), 1, "expected number of header comments")
+        self.assertEqual(len(header.O), 4, "defined positioner label rows")
+        self.assertEqual(len(header.H), 21, "defined metadata label rows")
+
+
+def suite(*args, **kw):
+    test_list = [
+        Issue123,
+        ]
+    test_suite = unittest.TestSuite()
+    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_spec.py b/tests/test_spec.py
index e0a9d07..e184d4f 100644
--- a/tests/test_spec.py
+++ b/tests/test_spec.py
@@ -101,23 +101,24 @@ class Test(unittest.TestCase):
         cmd = 'ascan  th 19.022 19.222  60 -20000'
         self.assertEqual(scan.scanCmd, cmd)
         self.assertEqual(sfile.getScanCommands([1,]), ['#S 1 '+cmd,])
-#         x = 'theta'
-#         y = 'signal'
-#         self.assertEqual(scan.column_first, x)
-#         self.assertEqual(scan.column_last, y)
-#         self.assertEqual(len(scan.data[x]), 61)
-#         self.assertEqual(scan.data[x][0], 19.022)
-#         self.assertEqual(scan.data[y][0], 0.0)
-#         self.assertEqual(scan.data[x][-1], 19.222)
-#         self.assertEqual(scan.data[y][-1], 0.0)
-#         scan = sfile.getScan(-1)
-#         self.assertEqual(len(scan.positioner), 22)
-#         x = 'yt3'
-#         y = 'zt1'
-#         self.assertEqual(scan.positioner.keys()[0], x)
-#         self.assertEqual(scan.positioner.keys()[-1], y)
-#         self.assertEqual(scan.positioner[x], 0.499275)
-#         self.assertEqual(scan.positioner[y], -113.52071)
+        x = 'theta'
+        y = 'signal'
+        self.assertEqual(scan.column_first, x)
+        self.assertEqual(scan.column_last, y)
+        self.assertEqual(len(scan.data[x]), 61)
+        self.assertEqual(scan.data[x][0], 19.022)
+        self.assertEqual(scan.data[y][0], 0.0)
+        self.assertEqual(scan.data[x][-1], 19.222)
+        self.assertEqual(scan.data[y][-1], 0.0)
+        scan = sfile.getScan(-1)
+        self.assertEqual(len(scan.positioner), 22)
+        x = '2-theta'
+        y = 'm22'
+        keys = list(scan.positioner.keys())
+        self.assertEqual(keys[0], x)
+        self.assertEqual(keys[-1], y)
+        self.assertEqual(scan.positioner[x], 67.78225)
+        self.assertEqual(scan.positioner[y], 1230.0415)
         # TODO: apply file-specific tests (see README.txt)
         # test hklscan (#14), hklmesh (#17)
 
@@ -269,23 +270,25 @@ class Test(unittest.TestCase):
         cmd = 'ascan  th 26.7108 27.1107  60 0.05'
         self.assertEqual(scan.scanCmd, cmd)
         self.assertEqual(sfile.getScanCommands([1,]), ['#S 1 '+cmd,])
-#         x = 'theta'
-#         y = 'imroi1'
-#         self.assertEqual(scan.column_first, x)
-#         self.assertEqual(scan.column_last, y)
-#         self.assertEqual(len(scan.data[x]), 60)
-#         self.assertEqual(scan.data[x][0], 26.714)
-#         self.assertEqual(scan.data[y][0], 6.0)
-#         self.assertEqual(scan.data[x][-1], 27.1075)
-#         self.assertEqual(scan.data[y][-1], 7.0)
-#         scan = sfile.getScan(-1)
-#         self.assertEqual(len(scan.positioner), 26)
-#         x = 'yt2'
-#         y = 'wst'
-#         self.assertEqual(scan.positioner.keys()[0], x)
-#         self.assertEqual(scan.positioner.keys()[-1], y)
-#         self.assertEqual(scan.positioner[x], -6.25e-05)
-#         self.assertEqual(scan.positioner[y], 2.9999031)
+        x = 'theta'
+        y = 'imroi1'
+        self.assertEqual(scan.column_first, x)
+        self.assertEqual(scan.column_last, y)
+        self.assertEqual(len(scan.data[x]), 60)
+        self.assertEqual(scan.data[x][0], 26.714)
+        self.assertEqual(scan.data[y][0], 6.0)
+        self.assertEqual(scan.data[x][-1], 27.1075)
+        self.assertEqual(scan.data[y][-1], 7.0)
+        scan = sfile.getScan(-1)
+        self.assertEqual(scan.scanCmd, "ascan  phi -180 180  720 0.5")
+        self.assertEqual(len(scan.positioner), 26)
+        x = '2-theta'
+        y = 'chSens'
+        keys = list(scan.positioner.keys())
+        self.assertEqual(keys[0], x)
+        self.assertEqual(keys[-1], y)
+        self.assertEqual(scan.positioner[x], 18.8)
+        self.assertEqual(scan.positioner[y], 5.0)
         # TODO: apply file-specific tests (see README.txt)
         # 1-D scans, text in #V35.2 metadata (powderMotor="theta" others are float), also has #UIM control lines
 
diff --git a/tests/test_specplot_gallery.py b/tests/test_specplot_gallery.py
index d96966b..4536f3e 100644
--- a/tests/test_specplot_gallery.py
+++ b/tests/test_specplot_gallery.py
@@ -58,6 +58,7 @@ class SpecPlotGallery(unittest.TestCase):
         sys.argv.append(self.tempdir)
         sys.argv.append(self.abs_data_fname('writer_1_3.h5'))
         specplot_gallery.main()
+        # this is HDF5 file, not SPEC, so not much content
         children = os.listdir(self.tempdir)
         self.assertEqual(len(children), 1)
         self.assertEqual(children[0], 'specplot_files_processing.log')

pip install -e .
parse_log_pytest
pytest
pytest pytest-cov
apt-get update apt-get install -y gcc
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
cached-property==1.5.2
certifi==2021.5.30
coverage==6.2
cycler==0.11.0
h5py==3.1.0
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
kiwisolver==1.3.1
matplotlib==3.3.4
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
numpy==1.19.5
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
Pillow==8.4.0
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
pytest-cov==4.0.0
python-dateutil==2.9.0.post0
six==1.17.0
-e git+https://github.com/prjemian/spec2nexus.git@e3bf9c68496b5128d427a043e24a9d9120f865e1#egg=spec2nexus
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
tomli==1.2.3
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

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
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - cached-property==1.5.2
      - coverage==6.2
      - cycler==0.11.0
      - h5py==3.1.0
      - kiwisolver==1.3.1
      - matplotlib==3.3.4
      - numpy==1.19.5
      - pillow==8.4.0
      - pytest-cov==4.0.0
      - python-dateutil==2.9.0.post0
      - six==1.17.0
      - tomli==1.2.3
prefix: /opt/conda/envs/spec2nexus

tests/issue123.py::Issue123::test_33id_file tests/issue123.py::Issue123::test_spock_file tests/test_specplot_gallery.py::SpecPlotGallery::test_command_line_spec_data_file_03_06_JanTest
tests/test_specplot_gallery.py::SpecPlotGallery::test_command_line_NeXus_writer_1_3
tests/issue107.py::Issue107::test_data_file tests/issue107.py::Issue133::test_data_file tests/test_spec.py::Test::test_33bm_spec tests/test_spec.py::Test::test_33id_spec tests/test_spec.py::Test::test_APS_spec_data tests/test_spec.py::Test::test_CdSe tests/test_spec.py::Test::test_YSZ011_ALDITO_Fe2O3_planar_fired_1 tests/test_spec.py::Test::test_custom_exceptions tests/test_spec.py::Test::test_extra_control_line_content__issue109 tests/test_spec.py::Test::test_file_initial_exceptions tests/test_spec.py::Test::test_isSpecFile tests/test_spec.py::Test::test_isSpecFileThis tests/test_spec.py::Test::test_lmn40 tests/test_spec.py::Test::test_strip_first_word tests/test_specplot_gallery.py::SpecPlotGallery::test_command_line_spec_data_file_02_03_setup tests/test_specplot_gallery.py::SpecPlotGallery::test_command_line_spec_data_file_33bm_spec tests/test_specplot_gallery.py::SpecPlotGallery::test_command_line_spec_data_file_list tests/test_specplot_gallery.py::SpecPlotGallery::test_command_line_spec_data_file_list_reversed_chronological_issue_79 tests/test_specplot_gallery.py::SpecPlotGallery::test_command_line_spec_data_file_user6idd tests/test_specplot_gallery.py::SpecPlotGallery::test_command_line_specified_directory_fails_isdir_issue_98 tests/test_specplot_gallery.py::SpecPlotGallery::test_command_line_specified_directory_not_found_issue_98
src/spec2nexus/plugins/spec_common_spec2nexus.py src/spec2nexus/charts.py .travis.yml CHANGES.rst src/spec2nexus/plugin.py src/spec2nexus/spec.py
src/spec2nexus/plugins/spec_common_spec2nexus.py src/spec2nexus/charts.py .travis.yml CHANGES.rst src/spec2nexus/plugin.py src/spec2nexus/spec.py