diff --git a/Makefile b/Makefile index badebe2a05fdac9b88700c92c481b7b202e2644c..9eb36408864ae93d1eb78ec47bc6d8f493122342 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,11 @@ motivation.html : motivation.md _layouts/slides.html $(INCLUDES) \ -o $@ $< +## unittest : Run unit test (for Python 2 and 3) +unittest: tools/check tools/validation_helpers.py tools/test_check.py + cd tools/ && python2 test_check.py + cd tools/ && python3 test_check.py + ## commands : Display available commands. commands : Makefile @sed -n 's/^##//p' $< diff --git a/tools/check b/tools/check index abd096ffddb509f5b5e6a853ea73410e311f84ae..ecfaaba9724a5971dc5f08007fb0bca1b6f3a644 100755 --- a/tools/check +++ b/tools/check @@ -406,7 +406,8 @@ class TopicPageValidator(MarkdownValidator): class MotivationPageValidator(MarkdownValidator): """Validate motivation.md""" DOC_HEADERS = {"layout": vh.is_str, - "title": vh.is_str} + "title": vh.is_str, + "subtitle": vh.is_str} # TODO: How to validate? May be a mix of reveal.js (HTML) + markdown. diff --git a/tools/test_check.py b/tools/test_check.py index 2f0966f68010dabbaa0c37b102acba04ce580cf4..003e26e82ef8086335a4c1e14565ad1c0a58acd7 100644 --- a/tools/test_check.py +++ b/tools/test_check.py @@ -1,6 +1,18 @@ #! /usr/bin/env python -import imp, logging, os, unittest +""" +Unit and functional tests for markdown lesson template validator. + +Some of these tests require looking for example files, which exist only on +the gh-pages branch. Some tests may therefore fail on branch "core". +""" + + +import imp +import logging +import os +import unittest + check = imp.load_source("check", # Import non-.py file os.path.join(os.path.dirname(__file__), "check")) @@ -8,7 +20,7 @@ check = imp.load_source("check", # Import non-.py file check.start_logging(level=logging.DEBUG) MARKDOWN_DIR = os.path.abspath( - os.path.join(os.path.dirname(__file__), os.pardir)) + os.path.join(os.path.dirname(__file__), os.pardir)) class BaseTemplateTest(unittest.TestCase): @@ -16,9 +28,6 @@ class BaseTemplateTest(unittest.TestCase): SAMPLE_FILE = "" # Path to a file that should pass all tests VALIDATOR = check.MarkdownValidator - def setUp(self): - self.sample_validator = self.VALIDATOR(self.SAMPLE_FILE) - def _create_validator(self, markdown): """Create validator object from markdown string; useful for failures""" return self.VALIDATOR(markdown=markdown) @@ -45,7 +54,8 @@ class TestIndexPage(BaseTemplateTest): VALIDATOR = check.IndexPageValidator def test_sample_file_passes_validation(self): - res = self.sample_validator.validate() + sample_validator = self.VALIDATOR(self.SAMPLE_FILE) + res = sample_validator.validate() self.assertTrue(res) def test_headers_missing_hrs(self): @@ -89,11 +99,33 @@ keywords: this is not a list # TESTS INVOLVING SECTION TITLES/HEADINGS def test_index_has_valid_section_headings(self): """The provided index page""" - res = self.sample_validator._validate_section_heading_order() + validator = self._create_validator("""## Topics + +1. [Topic Title One](01-one.html) +2. [Topic Title Two](02-two.html) + +## Other Resources + +* [Motivation](motivation.html) +* [Reference Guide](reference.html) +* [Next Steps](discussion.html) +* [Instructor's Guide](instructors.html)""") + res = validator._validate_section_heading_order() self.assertTrue(res) def test_index_fail_when_section_heading_absent(self): - res = self.sample_validator.ast.has_section_heading("Fake heading") + validator = self._create_validator("""## Topics + +1. [Topic Title One](01-one.html) +2. [Topic Title Two](02-two.html) + +## Other Resources + +* [Motivation](motivation.html) +* [Reference Guide](reference.html) +* [Next Steps](discussion.html) +* [Instructor's Guide](instructors.html)""") + res = validator.ast.has_section_heading("Fake heading") self.assertFalse(res) def test_fail_when_section_heading_is_wrong_level(self): @@ -122,7 +154,6 @@ Paragraph of introductory material. * [Instructor's Guide](instructors.html)""") self.assertFalse(validator._validate_section_heading_order()) - def test_fail_when_section_headings_in_wrong_order(self): validator = self._create_validator("""--- layout: lesson @@ -182,11 +213,15 @@ Paragraph of introductory material. # TESTS INVOLVING LINKS TO OTHER CONTENT def test_file_links_validate(self): - res = self.sample_validator._validate_links() + """Verify that all links in a sample file validate. + Involves checking for example files; may fail on "core" branch""" + sample_validator = self.VALIDATOR(self.SAMPLE_FILE) + res = sample_validator._validate_links() self.assertTrue(res) def test_html_link_to_extant_md_file_passes(self): - """Verify that an HTML link with corresponding MD file will pass""" + """Verify that an HTML link with corresponding MD file will pass + Involves checking for example files; may fail on "core" branch""" validator = self._create_validator("""[Topic Title One](01-one.html)""") self.assertTrue(validator._validate_links()) @@ -195,6 +230,8 @@ Paragraph of introductory material. For now this just tests that the regex handles #anchors. It doesn't validate that the named anchor exists in the md file + + Involves checking for example files; may fail on "core" branch """ validator = self._create_validator("""[Topic Title One](01-one.html#anchor)""") self.assertTrue(validator._validate_links()) @@ -206,7 +243,6 @@ Paragraph of introductory material. SQLite uses the integers 0 and 1 for the former, and represents the latter as discussed [earlier](#a:dates).""") self.assertTrue(validator._validate_links()) - def test_missing_markdown_file_fails_validation(self): """Fail validation when an html file is linked without corresponding markdown file""" @@ -227,7 +263,8 @@ SQLite uses the integers 0 and 1 for the former, and represents the latter as di self.assertFalse(validator._validate_links()) def test_finds_image_asset(self): - """Image asset is found""" + """Image asset is found in the expected file location + Involves checking for example files; may fail on "core" branch""" validator = self._create_validator( """""") self.assertTrue(validator._validate_links()) @@ -239,7 +276,9 @@ SQLite uses the integers 0 and 1 for the former, and represents the latter as di self.assertFalse(validator._validate_links()) def test_non_html_link_finds_csv(self): - """Look for CSV file in appropriate folder""" + """Look for CSV file in appropriate folder + Involves checking for example files; may fail on "core" branch + """ validator = self._create_validator( """Use [this CSV](data/data.csv) for the exercise.""") self.assertTrue(validator._validate_links()) @@ -257,7 +296,8 @@ class TestTopicPage(BaseTemplateTest): VALIDATOR = check.TopicPageValidator def test_sample_file_passes_validation(self): - res = self.sample_validator.validate() + sample_validator = self.VALIDATOR(self.SAMPLE_FILE) + res = sample_validator.validate() self.assertTrue(res) @@ -267,7 +307,8 @@ class TestMotivationPage(BaseTemplateTest): VALIDATOR = check.MotivationPageValidator def test_sample_file_passes_validation(self): - res = self.sample_validator.validate() + sample_validator = self.VALIDATOR(self.SAMPLE_FILE) + res = sample_validator.validate() self.assertTrue(res) @@ -326,7 +367,8 @@ Key Word 2 self.assertTrue(validator._validate_glossary()) def test_sample_file_passes_validation(self): - res = self.sample_validator.validate() + sample_validator = self.VALIDATOR(self.SAMPLE_FILE) + res = sample_validator.validate() self.assertTrue(res) @@ -336,7 +378,8 @@ class TestInstructorPage(BaseTemplateTest): VALIDATOR = check.InstructorPageValidator def test_sample_file_passes_validation(self): - res = self.sample_validator.validate() + sample_validator = self.VALIDATOR(self.SAMPLE_FILE) + res = sample_validator.validate() self.assertTrue(res) @@ -345,7 +388,8 @@ class TestLicensePage(BaseTemplateTest): VALIDATOR = check.LicensePageValidator def test_sample_file_passes_validation(self): - res = self.sample_validator.validate() + sample_validator = self.VALIDATOR(self.SAMPLE_FILE) + res = sample_validator.validate() self.assertTrue(res) def test_modified_file_fails_validation(self): @@ -361,7 +405,8 @@ class TestDiscussionPage(BaseTemplateTest): VALIDATOR = check.DiscussionPageValidator def test_sample_file_passes_validation(self): - res = self.sample_validator.validate() + sample_validator = self.VALIDATOR(self.SAMPLE_FILE) + res = sample_validator.validate() self.assertTrue(res)