Skip to content

Commit 1048da3

Browse files
committed
feat: lab3
1 parent cbf4ea9 commit 1048da3

File tree

10 files changed

+185
-93
lines changed

10 files changed

+185
-93
lines changed

.github/workflows/PR.yml

+19-15
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,31 @@ jobs:
1515
const { owner, repo, number: issue_number } = context.issue;
1616
const pr = await github.rest.pulls.get({ owner, repo, pull_number: issue_number });
1717
const title = pr.data.title;
18-
const labRegex = /\[LAB(\d+)\]/;
19-
const titleRegex = /^\[LAB\d+\] [\da-zA-Z]+$/;
2018
21-
if (!titleRegex.test(title)) {
22-
core.setFailed('PR title does not match the required format. Please use the format [LAB#] student#.');
19+
const titleRegex = /^\[LAB(\d+)\] [a-zA-Z]?\d+$/;
20+
const match = title.match(titleRegex);
21+
22+
let labNumberStr = undefined;
23+
if (match) {
24+
labNumberStr = match[1];
25+
} else {
26+
core.setFailed('PR title does not match the required format. Please use the format: [LAB#] <studentId>.');
2327
}
2428
25-
if (pr.data.head.ref !== pr.data.base.ref) {
26-
core.setFailed('The source branch and target branch must be the same.');
29+
const labelToAdd = `lab${labNumberStr}`;
30+
if (labNumberStr) {
31+
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [labelToAdd] });
2732
}
2833
2934
if (pr.data.base.ref === 'main') {
3035
core.setFailed('The target branch cannot be main.');
3136
}
3237
33-
const match = title.match(labRegex);
34-
if (match) {
35-
const labelToAdd = 'lab' + match[1];
36-
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [labelToAdd] });
37-
} else {
38-
core.setFailed('No match found in PR title. Please add a label in the format [LAB#] to the PR title.');
38+
if (labNumberStr < 3 && pr.data.head.ref !== pr.data.base.ref) {
39+
core.setFailed('The source branch and target branch must be the same.');
40+
}
41+
if (labNumberStr >= 3 && pr.data.head.ref !== labelToAdd) {
42+
core.setFailed(`The source branch must be '${labelToAdd}'`);
3943
}
4044
checklist-check:
4145
runs-on: ubuntu-latest
@@ -49,12 +53,12 @@ jobs:
4953
const pr = await github.rest.pulls.get({ owner, repo, pull_number: issue_number });
5054
const body = pr.data.body;
5155
52-
const checkboxes = body.match(/\- \[[x ]\]/g);
56+
const checkboxes = body.match(/^ ?(-|\*) \[[x ]\]/gmi);
5357
if (!checkboxes || checkboxes.length !== 5) {
5458
core.setFailed('The PR description must contain exactly 5 checkboxes.');
5559
}
5660
57-
const unchecked = body.match(/\- \[ \]/g);
61+
const unchecked = body.match(/^ ?(-|\*) \[ \]/gm);
5862
if (unchecked && unchecked.length > 0) {
59-
core.setFailed(`There are ${unchecked.length} unchecked items in the PR description.`);
63+
core.setFailed(`There are ${unchecked.length} unchecked item(s) in the PR description.`);
6064
}

.github/workflows/lab-autograding.yml

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Autograding
2+
3+
on:
4+
pull_request_target:
5+
types: [labeled, synchronize, opened, reopened, ready_for_review]
6+
7+
jobs:
8+
build:
9+
runs-on: ${{ matrix.os }}
10+
strategy:
11+
matrix:
12+
os: [ubuntu-22.04]
13+
fail-fast: false
14+
steps:
15+
- uses: actions/checkout@v4
16+
with:
17+
ref: "${{ github.event.pull_request.merge_commit_sha }}"
18+
fetch-depth: 1
19+
- uses: actions/setup-node@v4
20+
with:
21+
node-version: latest
22+
- name: Extract lab number and Check no changes other than specific files
23+
uses: actions/github-script@v5
24+
id: lab
25+
with:
26+
result-encoding: string
27+
github-token: ${{ secrets.GITHUB_TOKEN }}
28+
script: |
29+
const { owner, repo, number: issue_number } = context.issue;
30+
const pr = await github.rest.pulls.get({ owner, repo, pull_number: issue_number });
31+
const labels = pr.data.labels;
32+
const lab = labels.find((label) => label.name.startsWith('lab'));
33+
if (!lab) {
34+
core.setFailed('No lab label found on the PR.');
35+
return { number: 0 };
36+
}
37+
const labNumberMatch = lab.name.match(/lab(\d+)/);
38+
if (!labNumberMatch) {
39+
core.setFailed('Invalid lab label found on the PR.');
40+
return { number: 0 };
41+
}
42+
const labNumber = labNumberMatch[1];
43+
console.log(`Lab number: ${labNumber}`)
44+
45+
const files = await github.rest.pulls.listFiles({ owner, repo, pull_number: issue_number });
46+
const changedFiles = files.data.map((file) => file.filename);
47+
const allowedFileRegex = /^lab\d+\/main_test.js$/;
48+
if (!changedFiles.every((file) => allowedFileRegex.test(file))) {
49+
core.setFailed('The PR contains changes to files other than the allowed files.');
50+
}
51+
return labNumber;
52+
- name: Grading
53+
run: |
54+
cd lab${{ steps.lab.outputs.result }}
55+
./validate.sh

.github/workflows/lab1.yml

-26
This file was deleted.

.github/workflows/lab2.yml

-26
This file was deleted.

lab3/README.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Lab3
2+
3+
## Introduction
4+
5+
In this lab, you will write unit tests for functions implemented in `main.js`. You can learn how to use classes and functions in it by uncommenting the code in it. (But remember don't commit them on GitHub)
6+
7+
## Preparation (Important!!!)
8+
9+
1. Sync fork your branch (e.g., `SQLab:311XXXXXX`)
10+
2. `git checkout 311XXXXXX`
11+
3. `git pull`
12+
4. `git checkout -b lab3` (**NOT** your student ID !!!)
13+
14+
## Requirement
15+
16+
1. (40%) Write test cases in `main_test.js` and achieve 100% code coverage.
17+
2. (30%) For each function, parameterize its testcases to test the error-results.
18+
3. (30%) For each function, use at least 3 parameterized testcases to test the non-error-results.
19+
20+
You can run `validate.sh` in your local to test if you satisfy the requirements.
21+
22+
Please note that you must not alter files other than `main_test.js`. You will get 0 points if
23+
24+
1. you modify other files to achieve requirements.
25+
2. you can't pass all CI on your PR.
26+
27+
## Submission
28+
29+
You need to open a pull request to your branch (e.g. 311XXXXXX, your student number) and contain the code that satisfies the abovementioned requirements.
30+
31+
Moreover, please submit the URL of your PR to E3. Your submission will only be accepted when you present at both places.

lab3/main.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class Calculator {
2+
exp(x) {
3+
if (!Number.isFinite(x)) {
4+
throw Error('unsupported operand type');
5+
}
6+
const result = Math.exp(x);
7+
if (result === Infinity) {
8+
throw Error('overflow');
9+
}
10+
return result;
11+
}
12+
13+
log(x) {
14+
if (!Number.isFinite(x)) {
15+
throw Error('unsupported operand type');
16+
}
17+
const result = Math.log(x);
18+
if (result === -Infinity) {
19+
throw Error('math domain error (1)');
20+
}
21+
if (Number.isNaN(result)) {
22+
throw Error('math domain error (2)');
23+
}
24+
return result;
25+
}
26+
}
27+
28+
// const calculator = new Calculator();
29+
// console.log(calculator.exp(87));
30+
// console.log(calculator.log(48763));
31+
32+
module.exports = {
33+
Calculator
34+
};

lab3/main_test.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const { describe, it } = require('node:test');
2+
const assert = require('assert');
3+
const { Calculator } = require('./main');
4+
5+
// TODO: write your tests here

lab3/validate.sh

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
3+
# Check for unwanted files
4+
for file in *; do
5+
if [[ $file != "main.js" && $file != "main_test.js" && $file != "README.md" && $file != "validate.sh" ]]; then
6+
echo "[!] Unwanted file detected: $file."
7+
exit 1
8+
fi
9+
done
10+
11+
node=$(which node)
12+
test_path="${BASH_SOURCE[0]}"
13+
solution_path="$(realpath .)"
14+
tmp_dir=$(mktemp -d -t lab3-XXXXXXXXXX)
15+
16+
cd $tmp_dir
17+
18+
rm -rf *
19+
cp $solution_path/*.js .
20+
result=$($"node" --test --experimental-test-coverage) ; ret=$?
21+
if [ $ret -ne 0 ] ; then
22+
echo "[!] testing fails"
23+
exit 1
24+
else
25+
coverage=$(echo "$result" | grep 'all files' | awk -F '|' '{print $2}' | sed 's/ //g')
26+
if (( $(echo "$coverage < 100" | bc -l) )); then
27+
echo "[!] Coverage is only $coverage%"
28+
exit 1
29+
else
30+
echo "[V] Coverage is 100%"
31+
fi
32+
fi
33+
34+
rm -rf $tmp_dir
35+
36+
exit 0
37+
38+
# vim: set fenc=utf8 ff=unix et sw=2 ts=2 sts=2:

scripts/create-branches.sh

-23
This file was deleted.

scripts/merge-all.sh

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22

33
if [ $# -ne 1 ]; then
4-
echo "./merge-all.sh <commit-message>"
4+
echo "$0 <commit-message>"
55
exit 1
66
fi
77

@@ -17,7 +17,7 @@ for branch in $(git branch -r | grep -v HEAD); do
1717
echo "Checkout failed for branch $branch"
1818
exit 1
1919
fi
20-
git merge --squash main
20+
git merge --squash -s recursive -X theirs main
2121
if [[ $? -ne 0 ]]; then
2222
echo "Merge failed for branch $branch"
2323
exit 1
@@ -26,4 +26,4 @@ for branch in $(git branch -r | grep -v HEAD); do
2626
fi
2727
done
2828

29-
git checkout main
29+
git checkout main

0 commit comments

Comments
 (0)