From 6c51f8726c359cdd4652a690ffef2f4ff87a9070 Mon Sep 17 00:00:00 2001
From: Martin Budden <mjbudden@gmail.com>
Date: Sun, 8 Nov 2020 14:56:52 +0000
Subject: [PATCH 1/2] Updated tests.py to better support generic testing.

---
 scripts/tests.py | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/scripts/tests.py b/scripts/tests.py
index 56468f7..7dc46d9 100755
--- a/scripts/tests.py
+++ b/scripts/tests.py
@@ -90,6 +90,8 @@ def usage():
     sys.exit(1)
 
 def tests(tests):
+    doc_base_name = "readme"
+    doc_name = doc_base_name + ".md"
     scad_dir = "tests"
     deps_dir = scad_dir + "/deps"
     png_dir  = scad_dir + "/png"
@@ -97,7 +99,6 @@ def tests(tests):
     for dir in [deps_dir, png_dir, bom_dir]:
         if not os.path.isdir(dir):
             os.makedirs(dir)
-    doc_name = "readme.md"
     index = {}
     bodies = {}
     done = []
@@ -120,7 +121,7 @@ def tests(tests):
         base_name = scad[:-5]
         if not tests or base_name in tests:
             done.append(base_name)
-            print(base_name)
+            print('\n'+base_name)
             cap_name = base_name[0].capitalize() + base_name[1:]
             base_name = base_name.lower()
             scad_name = scad_dir + '/' + scad
@@ -135,6 +136,7 @@ def tests(tests):
             locations = [
                 ('vitamins/' + depluralise(base_name) + '.scad', 'Vitamins'),
                 ('printed/' + base_name + '.scad',               'Printed'),
+                ('tests/' + base_name + '.scad',                 'Tests'),
                 ('utils/' + base_name + '.scad',                 'Utilities'),
                 ('utils/core/' + base_name + '.scad',            'Core Utilities'),
             ]
@@ -288,10 +290,10 @@ See [usage](docs/usage.md) for requirements, installation instructions and a usa
         for type in types:
             for line in bodies[type]:
                 print(line, file = doc_file)
-    with open("readme.html", "wt") as html_file:
-        do_cmd("python -m markdown -x tables readme.md".split(), html_file)
+    with open(doc_base_name + ".html", "wt") as html_file:
+        do_cmd(("python -m markdown -x tables " + doc_name).split(), html_file)
     times.print_times()
-    do_cmd('codespell -L od readme.md'.split())
+    do_cmd(('codespell -L od ' + doc_name).split())
 
 if __name__ == '__main__':
     for arg in sys.argv[1:]:

From 5fa33d7c4ddca8888d46dc938c9344ab0b52c900 Mon Sep 17 00:00:00 2001
From: Chris Palmer <nop.head@gmail.com>
Date: Mon, 9 Nov 2020 16:17:02 +0000
Subject: [PATCH 2/2] Tests.py now works in projects and makes tests.md and
 tests.html. NopSCADlib blurb now scraped from libtest.scad. libtest.scad no
 longer required and lack of it is used to detect a project.

---
 libtest.scad     | 17 +++++++++++
 readme.md        | 10 ++++---
 scripts/tests.py | 78 ++++++++++++++++++++++++------------------------
 3 files changed, 62 insertions(+), 43 deletions(-)

diff --git a/libtest.scad b/libtest.scad
index 2a65f0e..53e7a41 100644
--- a/libtest.scad
+++ b/libtest.scad
@@ -17,6 +17,23 @@
 // If not, see <https://www.gnu.org/licenses/>.
 //
 
+//!# NopSCADlib
+//! An ever expanding library of parts modelled in OpenSCAD useful for 3D printers and enclosures for electronics, etc.
+//!
+//! It contains lots of vitamins (the RepRap term for non-printed parts), some general purpose printed parts and some utilities.
+//! There are also Python scripts to generate Bills of Materials (BOMs),
+//! STL files for all the printed parts, DXF files for CNC routed parts in a project and a manual containing assembly
+//! instructions and exploded views by scraping markdown embedded in OpenSCAD comments, [see scripts](scripts/readme.md).
+//!
+//! A simple example project can be found [here](examples/MainsBreakOutBox/readme.md).
+//!
+//! For more examples of what it can make see the [gallery](gallery/readme.md).
+//!
+//! The license is GNU General Public License v3.0, see [COPYING](COPYING).
+//!
+//! See [usage](docs/usage.md) for requirements, installation instructions and a usage guide.
+//!
+//! <img src="libtest.png" width="100%"/>
 //
 // This file shows all the parts in the library.
 //
diff --git a/readme.md b/readme.md
index c4d41c3..5fb7925 100644
--- a/readme.md
+++ b/readme.md
@@ -1,10 +1,12 @@
 # NopSCADlib
 An ever expanding library of parts modelled in OpenSCAD useful for 3D printers and enclosures for electronics, etc.
 
-It contains lots of vitamins (the RepRap term for non-printed parts), some general purpose printed parts and
-some utilities. There are also Python scripts to generate Bills of Materials (BOMs),
- STL files for all the printed parts, DXF files for CNC routed parts in a project and a manual containing assembly
-instructions and exploded views by scraping markdown embedded in OpenSCAD comments, [see scripts](scripts/readme.md). A simple example project can be found [here](examples/MainsBreakOutBox/readme.md).
+It contains lots of vitamins (the RepRap term for non-printed parts), some general purpose printed parts and some utilities.
+There are also Python scripts to generate Bills of Materials (BOMs),
+STL files for all the printed parts, DXF files for CNC routed parts in a project and a manual containing assembly
+instructions and exploded views by scraping markdown embedded in OpenSCAD comments, [see scripts](scripts/readme.md).
+
+A simple example project can be found [here](examples/MainsBreakOutBox/readme.md).
 
 For more examples of what it can make see the [gallery](gallery/readme.md).
 
diff --git a/scripts/tests.py b/scripts/tests.py
index 7dc46d9..1813794 100755
--- a/scripts/tests.py
+++ b/scripts/tests.py
@@ -90,8 +90,6 @@ def usage():
     sys.exit(1)
 
 def tests(tests):
-    doc_base_name = "readme"
-    doc_name = doc_base_name + ".md"
     scad_dir = "tests"
     deps_dir = scad_dir + "/deps"
     png_dir  = scad_dir + "/png"
@@ -109,14 +107,28 @@ def tests(tests):
     #
     png_name = "libtest.png"
     scad_name = "libtest.scad"
-    if not os.path.isfile(png_name):
-        openscad.run(colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,50,0,340,500", "--autocenter", "--viewall", "-o", png_name, scad_name);
-        do_cmd(["magick", png_name, "-trim", "-resize", "1280", "-bordercolor", background, "-border", "10", png_name])
+    if os.path.isfile(scad_name):
+        libtest = True
+        lib_blurb = scrape_blurb(scad_name)
+        if not os.path.isfile(png_name):
+            openscad.run(colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,50,0,340,500", "--autocenter", "--viewall", "-o", png_name, scad_name);
+            do_cmd(["magick", png_name, "-trim", "-resize", "1280", "-bordercolor", background, "-border", "10", png_name])
+    else:
+        #
+        # Project tests so just a title
+        #
+        libtest = False
+        project = ' '.join(word[0].upper() + word[1:] for word in os.path.basename(os.getcwd()).split('_'))
+        lib_blurb = '#' + project + ' Tests\n'
+
+    doc_base_name = "readme" if libtest else "tests"
+    doc_name = doc_base_name + ".md"
     #
     # List of individual part files
     #
-    scads = [i for i in sorted(os.listdir(scad_dir), key = lambda s: s.lower()) if i[-5:] == ".scad"]
 
+    scads = [i for i in sorted(os.listdir(scad_dir), key = lambda s: s.lower()) if i[-5:] == ".scad"]
+    types = []
     for scad in scads:
         base_name = scad[:-5]
         if not tests or base_name in tests:
@@ -133,13 +145,15 @@ def tests(tests):
             if is_plural(base_name) and os.path.isfile(vits_name):
                 objects_name = vits_name
 
-            locations = [
-                ('vitamins/' + depluralise(base_name) + '.scad', 'Vitamins'),
-                ('printed/' + base_name + '.scad',               'Printed'),
-                ('tests/' + base_name + '.scad',                 'Tests'),
-                ('utils/' + base_name + '.scad',                 'Utilities'),
-                ('utils/core/' + base_name + '.scad',            'Core Utilities'),
-            ]
+            locations = []
+            if os.path.isdir('vitamins'):
+                locations.append(('vitamins/' + depluralise(base_name) + '.scad', 'Vitamins'))
+            if os.path.isdir('printed'):
+                locations.append(('printed/' + base_name + '.scad',               'Printed'))
+            if os.path.isdir('utils'):
+                locations.append(('utils/' + base_name + '.scad',                 'Utilities'))
+            if libtest and os.path.isdir('utils/core'):
+                locations.append(('utils/core/' + base_name + '.scad',            'Core Utilities'))
 
             for name, type in locations:
                 if os.path.isfile(name):
@@ -149,14 +163,17 @@ def tests(tests):
                 print("Can't find implementation!")
                 continue
 
-            vsplit = "AJR" + chr(ord('Z') + 1)
-            vtype = locations[0][1]
-            types = [vtype + ' ' + vsplit[i] + '-'  + chr(ord(vsplit[i + 1]) - 1) for i in range(len(vsplit) - 1)] + [loc[1] for loc in locations[1 :]]
-            if type == vtype:
-                for i in range(1, len(vsplit)):
-                    if cap_name[0] < vsplit[i]:
-                         type = types[i - 1]
-                         break
+            if libtest:
+                vsplit = "AJR" + chr(ord('Z') + 1)
+                vtype = locations[0][1]
+                types = [vtype + ' ' + vsplit[i] + '-' + chr(ord(vsplit[i + 1]) - 1) for i in range(len(vsplit) - 1)] + [loc[1] for loc in locations[1 :]]
+                if type == vtype:
+                    for i in range(1, len(vsplit)):
+                        if cap_name[0] < vsplit[i]:
+                             type = types[i - 1]
+                             break
+            else:
+                types = [loc[1] for loc in locations]
 
             for t in types:
                 if not t in bodies:
@@ -252,24 +269,7 @@ def tests(tests):
         usage()
 
     with open(doc_name, "wt") as doc_file:
-        print('# NopSCADlib', file = doc_file)
-        print('''\
-An ever expanding library of parts modelled in OpenSCAD useful for 3D printers and enclosures for electronics, etc.
-
-It contains lots of vitamins (the RepRap term for non-printed parts), some general purpose printed parts and
-some utilities. There are also Python scripts to generate Bills of Materials (BOMs),
- STL files for all the printed parts, DXF files for CNC routed parts in a project and a manual containing assembly
-instructions and exploded views by scraping markdown embedded in OpenSCAD comments, [see scripts](scripts/readme.md). A simple example project can be found [here](examples/MainsBreakOutBox/readme.md).
-
-For more examples of what it can make see the [gallery](gallery/readme.md).
-
-The license is GNU General Public License v3.0, see [COPYING](COPYING).
-
-See [usage](docs/usage.md) for requirements, installation instructions and a usage guide.
-
-<img src="libtest.png" width="100%"/>\n
-''', file = doc_file)
-
+        print(lib_blurb, file = doc_file)
         print('## Table of Contents<a name="top"/>', file = doc_file)
         print('<table><tr>', file = doc_file)
         n = 0