Skip to content
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
16 changes: 16 additions & 0 deletions branch_compare/.gitmastery-exercise.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"exercise_name": "branch-compare",
"tags": ["git-branch", "git-diff"],
"requires_git": true,
"requires_github": false,
"base_files": {
"answers.txt": "answers.txt"
},
"exercise_repo": {
"repo_type": "local",
"repo_name": "data_streams",
"repo_title": null,
"create_fork": null,
"init": true
}
}
9 changes: 9 additions & 0 deletions branch_compare/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# branch-compare

## Task

You are recording a numerical data stream from two sources. The data are stored in `data.txt`, using a different branch for each stream. The two data streams are supposed to be identical but can vary on rare occasions.

Answer the questions given in `answers.txt`.

Run `gitmastery verify` to check if your answers are correct.
Empty file added branch_compare/__init__.py
Empty file.
62 changes: 62 additions & 0 deletions branch_compare/download.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from exercise_utils.cli import run_command
from exercise_utils.git import add, commit, checkout
from exercise_utils.file import append_to_file
from exercise_utils.gitmastery import create_start_tag

import random

__resources__ = {}

def get_sequence(n=1000, digits=8, seed=None):
rng = random.Random(seed)
lo, hi = 10**(digits - 1), 10**digits - 1
return rng.sample(range(lo, hi + 1), k=n)

def get_modified_sequence(seq, digits=8, idx=None, seed=None):
rng = random.Random(seed)
n = len(seq)
if idx is None:
idx = rng.randrange(n)

modified = seq.copy()
seen = set(seq)
lo, hi = 10**(digits - 1), 10**digits - 1

old = modified[idx]
new = old
while new in seen:
new = rng.randint(lo, hi)
modified[idx] = new
return modified


def setup(verbose: bool = False):

create_start_tag(verbose)

orig_data = get_sequence()
modified_data = get_modified_sequence(orig_data)

run_command(["touch", "data.txt"], verbose)
add(["data.txt"], verbose)
commit("Add empty data.txt", verbose)
checkout("stream-1", True, verbose)

for i in orig_data:
append_to_file("data.txt", str(i)+"\n")

add(["data.txt"], verbose)
commit("Add data to data.txt", verbose)


checkout("main", False, verbose)
checkout("stream-2", True, verbose)

for i in modified_data:
append_to_file("data.txt", str(i)+"\n")

add(["data.txt"], verbose)
commit("Add data to data.txt", verbose)

checkout("main", False, verbose)

5 changes: 5 additions & 0 deletions branch_compare/res/answers.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Q: Which number is present in branch stream-1 but not in branch stream-2?
A:

Q: Which number is present in branch stream-2 but not in branch stream-1?
A:
Empty file.
6 changes: 6 additions & 0 deletions branch_compare/tests/specs/base.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
initialization:
steps:
- type: commit
empty: true
message: Empty commit
id: start
66 changes: 66 additions & 0 deletions branch_compare/tests/test_verify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from git_autograder import GitAutograderStatus, GitAutograderTestLoader, assert_output
from unittest.mock import patch
from git_autograder.answers.rules.has_exact_value_rule import HasExactValueRule

from ..verify import verify, QUESTION_ONE, QUESTION_TWO

REPOSITORY_NAME = "branch-compare"

loader = GitAutograderTestLoader(__file__, REPOSITORY_NAME, verify)


def test_base():
with (
patch("branch_compare.verify.has_made_changes", return_value=False),
patch("branch_compare.verify.get_stream1_diff", return_value="12345"),
patch("branch_compare.verify.get_stream2_diff", return_value="98765"),
loader.load(
"specs/base.yml",
"start",
mock_answers={
QUESTION_ONE: "12345",
QUESTION_TWO: "98765",
}
) as output,
):
assert_output(output, GitAutograderStatus.SUCCESSFUL)

def test_wrong_stream1_diff():
with (
patch("branch_compare.verify.has_made_changes", return_value=False),
patch("branch_compare.verify.get_stream1_diff", return_value="99999"),
patch("branch_compare.verify.get_stream2_diff", return_value="98765"),
loader.load(
"specs/base.yml",
"start",
mock_answers={
QUESTION_ONE: "12345",
QUESTION_TWO: "98765",
}
) as output,
):
assert_output(
output,
GitAutograderStatus.UNSUCCESSFUL,
[HasExactValueRule.NOT_EXACT.format(question=QUESTION_ONE)],
)

def test_wrong_stream2_diff():
with (
patch("branch_compare.verify.has_made_changes", return_value=False),
patch("branch_compare.verify.get_stream1_diff", return_value="12345"),
patch("branch_compare.verify.get_stream2_diff", return_value="99999"),
loader.load(
"specs/base.yml",
"start",
mock_answers={
QUESTION_ONE: "12345",
QUESTION_TWO: "98765",
}
) as output,
):
assert_output(
output,
GitAutograderStatus.UNSUCCESSFUL,
[HasExactValueRule.NOT_EXACT.format(question=QUESTION_TWO)],
)
72 changes: 72 additions & 0 deletions branch_compare/verify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from git_autograder import (
GitAutograderOutput,
GitAutograderExercise,
GitAutograderStatus,
)

from git_autograder.answers.rules import HasExactValueRule, NotEmptyRule


QUESTION_ONE = "Which number is present in branch stream-1 but not in branch stream-2?"
QUESTION_TWO = "Which number is present in branch stream-2 but not in branch stream-1?"

FILE_PATH = "data.txt"
BRANCH_1 = "stream-1"
BRANCH_2 = "stream-2"

def has_made_changes(exercise: GitAutograderExercise) -> bool:
repo = exercise.repo.repo

for bname in (BRANCH_1, BRANCH_2):
if not exercise.repo.branches.has_branch(bname):
return True

head = repo.commit(bname)
if len(head.parents) != 1:
return True

return False

def get_branch_diff(exercise: GitAutograderExercise, branch1: str, branch2: str) -> str:
exercise.repo.branches.branch(branch1).checkout()
with exercise.repo.files.file(FILE_PATH) as f1:
contents1 = f1.read()

exercise.repo.branches.branch(branch2).checkout()
with exercise.repo.files.file(FILE_PATH) as f2:
contents2 = f2.read()

exercise.repo.branches.branch("main").checkout()

set1 = {line.strip() for line in contents1.splitlines() if line.strip()}
set2 = {line.strip() for line in contents2.splitlines() if line.strip()}
diff = set1 - set2
return str(diff.pop())

def get_stream1_diff(exercise: GitAutograderExercise) -> str:
return get_branch_diff(exercise, BRANCH_1, BRANCH_2)

def get_stream2_diff(exercise: GitAutograderExercise) -> str:
return get_branch_diff(exercise, BRANCH_2, BRANCH_1)

def verify(exercise: GitAutograderExercise) -> GitAutograderOutput:

if has_made_changes(exercise):
return exercise.to_output(["No changes are supposed to be made to the two branches in this exercise"], GitAutograderStatus.UNSUCCESSFUL)

exercise.repo.branches.branch("main").checkout()

ans_1 = get_stream1_diff(exercise)
ans_2 = get_stream2_diff(exercise)

exercise.answers.add_validation(
QUESTION_ONE,
NotEmptyRule(),
HasExactValueRule(ans_1),
).add_validation(
QUESTION_TWO,
NotEmptyRule(),
HasExactValueRule(ans_2),
).validate()

return exercise.to_output(["Great work comparing the branches successfully!"], GitAutograderStatus.SUCCESSFUL)