Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ease building #351

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
pytopt?=

fetch_all_branches:
git branch -r | grep -v '\->' | sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" | while read remote; do git branch --track "$${remote#origin/}" "$$remote"; done


tdd:
git ls-files | entr make test pytopt='-x --lf'


html:
asciidoctor \
-a stylesheet=theme/asciidoctor-clean.custom.css \
Expand All @@ -8,7 +18,7 @@ html:
*.asciidoc

test: html
pytest tests.py --tb=short -vv
pytest tests.py --tb=short -vv $(pytopt)

update-code:
# git submodule update --init --recursive
Expand Down
212 changes: 126 additions & 86 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,61 @@
from chapters import CHAPTERS, BRANCHES, STANDALONE, NO_EXERCISE



def all_branches():
return subprocess.run(
['git', 'branch', '-a'],
cwd=Path(__file__).parent / 'code',
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True
).stdout.decode().split()
return (
subprocess.run(
["git", "branch", "-a"],
cwd=Path(__file__).parent / "code",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
)
.stdout.decode()
.split()
)


def git_log(chapter):
return subprocess.run(
['git', 'log', chapter, '--oneline', '--decorate'],
cwd=Path(__file__).parent / 'code',
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True
["git", "log", chapter, "--oneline", "--decorate"],
cwd=Path(__file__).parent / "code",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
).stdout.decode()

@pytest.fixture(scope='session')

@pytest.fixture(scope="session")
def master_log():
return git_log('master')
return git_log("master")


@pytest.mark.parametrize('chapter', CHAPTERS)
@pytest.mark.xfail(reason="Temp skip so as to keep -x option")
@pytest.mark.parametrize("chapter", CHAPTERS)
def test_master_has_all_chapters_in_its_history(master_log, chapter):
if chapter in BRANCHES:
return
assert f'{chapter})' in master_log
assert f"{chapter})" in master_log

@pytest.mark.parametrize('chapter', CHAPTERS)

@pytest.mark.xfail(reason="Temp skip so as to keep -x option")
@pytest.mark.parametrize("chapter", CHAPTERS)
def test_exercises_for_reader(chapter):
exercise_branch = f'{chapter}_exercise'
exercise_branch = f"{chapter}_exercise"
branches = all_branches()
if chapter in NO_EXERCISE:
if exercise_branch in branches:
pytest.fail(f'looks like there is an exercise for {chapter} after all!')
pytest.fail(f"looks like there is an exercise for {chapter} after all!")
else:
pytest.xfail(f'{chapter} has no exercise yet')
pytest.xfail(f"{chapter} has no exercise yet")
return
assert exercise_branch in branches
assert f'{chapter})' in git_log(exercise_branch), f'Exercise for {chapter} not up to date'
assert f"{chapter})" in git_log(
exercise_branch
), f"Exercise for {chapter} not up to date"


def previous_chapter(chapter):
chapter_no = CHAPTERS.index(chapter)
Expand All @@ -58,15 +74,16 @@ def previous_chapter(chapter):
previous = CHAPTERS[chapter_no - 2]
return previous

@pytest.mark.parametrize('chapter', CHAPTERS)

@pytest.mark.parametrize("chapter", CHAPTERS)
def test_each_chapter_follows_the_last(chapter):
previous = previous_chapter(chapter)
if previous is None:
return
assert f'{previous})' in git_log(chapter), f'{chapter} did not follow {previous}'
assert f"{previous})" in git_log(chapter), f"{chapter} did not follow {previous}"


@pytest.mark.parametrize('chapter', CHAPTERS + STANDALONE)
@pytest.mark.parametrize("chapter", CHAPTERS + STANDALONE)
def test_chapter(chapter):
for listing in parse_listings(chapter):
check_listing(listing, chapter)
Expand All @@ -75,64 +92,76 @@ def test_chapter(chapter):
@contextmanager
def checked_out(chapter):
subprocess.run(
['git', 'checkout', f'{chapter}'],
cwd=Path(__file__).parent / 'code',
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True
["git", "checkout", f"{chapter}"],
cwd=Path(__file__).parent / "code",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
)
try:
yield

finally:
subprocess.run(
['git', 'checkout', '-'],
cwd=Path(__file__).parent / 'code',
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True
["git", "checkout", "-"],
cwd=Path(__file__).parent / "code",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
)


def tree_for_branch(chapter_name):
with checked_out(chapter_name):
return subprocess.run(
['tree', '-v', '-I', '__pycache__|*.egg-info'],
cwd=Path(__file__).parent / 'code',
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True
["tree", "-v", "-I", "__pycache__|*.egg-info"],
cwd=Path(__file__).parent / "code",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
).stdout.decode()


def check_listing(listing, chapter):
if 'tree' in listing.classes:
if "tree" in listing.classes:
actual_contents = tree_for_branch(chapter)
elif 'non-head' in listing.classes:
elif "non-head" in listing.classes:
actual_contents = file_contents_for_tag(
listing.filename, chapter, listing.tag,
listing.filename,
chapter,
listing.tag,
)
elif 'existing' in listing.classes:
elif "existing" in listing.classes:
actual_contents = file_contents_for_previous_chapter(
listing.filename, chapter,
listing.filename,
chapter,
)
elif listing.is_diff:
actual_contents = diff_for_tag(
listing.filename, chapter, listing.tag,
listing.filename,
chapter,
listing.tag,
)

else:
actual_contents = file_contents_for_branch(listing.filename, chapter)
actual_lines = actual_contents.split('\n')
actual_lines = actual_contents.split("\n")

if '...' in listing.contents:
for section in re.split(r'#?\.\.\.', listing.fixed_contents):
if "..." in listing.contents:
for section in re.split(r"#?\.\.\.", listing.fixed_contents):
lines = section.splitlines()
if section.strip() not in actual_contents:
assert lines == actual_lines, \
f'section from [{listing.tag}] not found within actual'
assert (
lines == actual_lines
), f"section from [{listing.tag}] not found within actual"

elif listing.fixed_contents not in actual_contents:
assert listing.lines == actual_lines, \
f'listing [{listing.tag}] not found within actual'

assert (
listing.lines == actual_lines
), f"listing [{listing.tag}] not found within actual"


@dataclass
Expand All @@ -143,85 +172,96 @@ class Listing:
classes: list
is_diff: bool

callouts = re.compile(r' #?(\(\d\) ?)+$', flags=re.MULTILINE)
callouts_alone = re.compile(r'^\(\d\)$')
callouts = re.compile(r" #?(\(\d\) ?)+$", flags=re.MULTILINE)
callouts_alone = re.compile(r"^\(\d\)$")

@property
def fixed_contents(self):
fixed = self.contents
fixed = self.callouts.sub('', fixed)
fixed = '\n'.join(
l for l in fixed.splitlines()
if not self.callouts_alone.match(l)
fixed = self.callouts.sub("", fixed)
fixed = "\n".join(
l for l in fixed.splitlines() if not self.callouts_alone.match(l)
)
return fixed

@property
def lines(self):
return self.fixed_contents.split('\n')
return self.fixed_contents.split("\n")


def parse_listings(chapter_name):
raw_contents = Path(f'{chapter_name}.html').read_text()
raw_contents = Path(f"{chapter_name}.html").read_text()
parsed_html = html.fromstring(raw_contents)

for listing_node in parsed_html.cssselect('.exampleblock'):
[block_node] = listing_node.cssselect('.listingblock')
classes = block_node.get('class').split()
if 'skip' in classes:
for listing_node in parsed_html.cssselect(".exampleblock"):
[block_node] = listing_node.cssselect(".listingblock")
classes = block_node.get("class").split()
if "skip" in classes:
continue

if 'tree' in classes:
if "tree" in classes:
filename = None
else:
[title_node] = listing_node.cssselect('.title')
[title_node] = listing_node.cssselect(".title")
title = title_node.text_content()
print('found listing', title)
print("found listing", title)
try:
filename = re.search(r'.+ \((.+)\)', title).group(1)
filename = re.search(r".+ \((.+)\)", title).group(1)
except AttributeError as e:
raise AssertionError(f'Could not find filename in title {title}') from e
raise AssertionError(f"Could not find filename in title {title}") from e

is_diff = bool(listing_node.cssselect('code[data-lang="diff"]'))
tag = listing_node.get('id')
tag = listing_node.get("id")

[code_node] = block_node.cssselect('.content pre')
[code_node] = block_node.cssselect(".content pre")
yield Listing(
filename, tag, contents=code_node.text_content(), classes=classes,
is_diff=is_diff
filename,
tag,
contents=code_node.text_content(),
classes=classes,
is_diff=is_diff,
)


def file_contents_for_branch(filename, chapter_name):
return subprocess.run(
['git', 'show', f'{chapter_name}:{filename}'],
cwd=Path(__file__).parent / 'code',
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True
["git", "show", f"{chapter_name}:{filename}"],
cwd=Path(__file__).parent / "code",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
).stdout.decode()


def file_contents_for_previous_chapter(filename, chapter_name):
previous = previous_chapter(chapter_name)
return file_contents_for_branch(filename, previous)


def file_contents_for_tag(filename, chapter_name, tag):
output = subprocess.run(
['git', 'show', f'{chapter_name}^{{/\\[{tag}\\]}}:{filename}'],
cwd=Path(__file__).parent / 'code',
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True
["git", "show", f"{chapter_name}^{{/\\[{tag}\\]}}:{filename}"],
cwd=Path(__file__).parent / "code",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
).stdout.decode()
assert output.strip(), f'no commit found for [{tag}]'
assert output.strip(), f"no commit found for [{tag}]"
return output


def diff_for_tag(filename, chapter_name, tag):
if tag.endswith('_diff'):
if tag.endswith("_diff"):
tag = tag[:-5]
output = subprocess.run(
['git', 'show', f'{chapter_name}^{{/\\[{tag}\\]}}', '--', filename],
cwd=Path(__file__).parent / 'code',
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True
["git", "show", f"{chapter_name}^{{/\\[{tag}\\]}}", "--", filename],
cwd=Path(__file__).parent / "code",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
).stdout.decode()
assert output.strip(), f'no commit found for [{tag}]'
return '\n'.join(l.rstrip() for l in output.splitlines())
assert output.strip(), f"no commit found for [{tag}]"
return "\n".join(l.rstrip() for l in output.splitlines())