Skip to content

Commit e33b934

Browse files
committed
feat: lab1
1 parent b6cf075 commit e33b934

File tree

9 files changed

+294
-0
lines changed

9 files changed

+294
-0
lines changed

.github/pull_request_template.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## Description
2+
3+
<!-- You can check sample PR here: (remember delete this link when you create your PR) -->
4+
<https://github.com/SQLab/112-spring-software-testing-and-secure-programming/pull/1>
5+
6+
<!-- Please briefly describe your change here -->
7+
8+
---
9+
10+
<!-- Please make sure you're satisfy and fill the following checkboxes -->
11+
<!-- A good PR should include the following parts: -->
12+
13+
- [ ] A clear title (name your PR "[LAB{lab_number}] {your_student_id}")
14+
- [ ] A meaningful message for PR, as well as its commits
15+
- [ ] From your specific branch (***not main or other's branch***) merging to your branch
16+
- [ ] Excluding any irrelevant files, such as binaries, text files, or dot files
17+
- [ ] Passing all CI

.github/workflows/PR.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: PR automation
2+
on:
3+
pull_request_target:
4+
types: [opened, reopened, edited, ready_for_review]
5+
6+
jobs:
7+
labeler:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Label PR
11+
uses: actions/github-script@v5
12+
with:
13+
github-token: ${{ secrets.PAT }}
14+
script: |
15+
const { owner, repo, number: issue_number } = context.issue;
16+
const pr = await github.rest.pulls.get({ owner, repo, pull_number: issue_number });
17+
const title = pr.data.title;
18+
const labRegex = /\[LAB(\d+)\]/;
19+
const titleRegex = /\[LAB\d+\] [\da-zA-Z]+/;
20+
21+
if (!titleRegex.test(title)) {
22+
core.setFailed('PR title does not match the required format. Please use the format [LAB#] student#.');
23+
}
24+
25+
if (pr.data.head.ref !== pr.data.base.ref) {
26+
core.setFailed('The source branch and target branch must be the same.');
27+
}
28+
29+
if (pr.data.base.ref === 'main') {
30+
core.setFailed('The target branch cannot be main.');
31+
}
32+
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.');
39+
}
40+
checklist-check:
41+
runs-on: ubuntu-latest
42+
steps:
43+
- name: Check PR description
44+
uses: actions/github-script@v5
45+
with:
46+
github-token: ${{ secrets.GITHUB_TOKEN }}
47+
script: |
48+
const { owner, repo, number: issue_number } = context.issue;
49+
const pr = await github.rest.pulls.get({ owner, repo, pull_number: issue_number });
50+
const body = pr.data.body;
51+
52+
const checkboxes = body.match(/\- \[[x ]\]/g);
53+
if (!checkboxes || checkboxes.length !== 5) {
54+
core.setFailed('The PR description must contain exactly 5 checkboxes.');
55+
}
56+
57+
const unchecked = body.match(/\- \[ \]/g);
58+
if (unchecked && unchecked.length > 0) {
59+
core.setFailed(`There are ${unchecked.length} unchecked items in the PR description.`);
60+
}

.github/workflows/lab1.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: lab1 autograding
2+
3+
on:
4+
pull_request:
5+
types: [labeled, synchronize, opened, reopened, ready_for_review]
6+
7+
jobs:
8+
build:
9+
if: contains(github.event.pull_request.labels.*.name, 'lab1')
10+
runs-on: ${{ matrix.os }}
11+
strategy:
12+
matrix:
13+
os: [ubuntu-22.04]
14+
fail-fast: false
15+
steps:
16+
- uses: actions/checkout@v1
17+
with:
18+
fetch-depth: 1
19+
- name: dependency (ubuntu)
20+
run: |
21+
curl -fsSL https://deb.nodesource.com/setup_21.x | sudo -E bash - &&\
22+
sudo apt-get install -y nodejs
23+
- name: grading
24+
run: |
25+
echo "cd lab1"
26+
cd lab1
27+
./validate.sh

lab1/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Lab1
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 `main.js.` (But remember don't commit them on GitHub)
6+
7+
## Requirement
8+
9+
1. Write test cases in `main_test.js` and achieve 100% code coverage. (100%)
10+
11+
You can run `validate.sh` in your local to test if you satisfy the requirements.
12+
13+
Please note that you must not alter files other than `main_test.js`. You will get 0 points if
14+
15+
1. you modify other files to achieve requirements.
16+
2. you can't pass all CI on your PR.
17+
18+
## Submission
19+
20+
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.
21+
22+
Moreover, please submit the URL of your PR to E3. Your submission will only be accepted when you present at both places.

lab1/main.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// NOTICE: DO NOT MODIFY THE CODE IN THIS FILE
2+
// But you can uncomment code below and run this file to understand how to use the classes
3+
4+
class MyClass {
5+
constructor() {
6+
this.students = [];
7+
}
8+
9+
addStudent(student) {
10+
if (!(student instanceof Student)) {
11+
return -1;
12+
}
13+
this.students.push(student);
14+
return this.students.length - 1;
15+
}
16+
17+
getStudentById(id) {
18+
if (id < 0 || id >= this.students.length) {
19+
return null;
20+
}
21+
return this.students[id];
22+
}
23+
}
24+
25+
class Student {
26+
constructor() {
27+
this.name = undefined;
28+
}
29+
30+
setName(userName) {
31+
if (typeof userName !== 'string') {
32+
return;
33+
}
34+
this.name = userName;
35+
}
36+
37+
getName() {
38+
if (this.name === undefined) {
39+
return '';
40+
}
41+
return this.name;
42+
}
43+
}
44+
45+
// const myClass = new MyClass();
46+
// const names = ['John', 'Jane', 'Doe', 'Smith'];
47+
// names.forEach(name => {
48+
// const student = new Student();
49+
// student.setName(name);
50+
// const newStudentId = myClass.addStudent(student);
51+
// const newStudentName = myClass.getStudentById(newStudentId).getName();
52+
// console.log('[+] Added student with id: %d, name: %s', newStudentId, newStudentName);
53+
// });
54+
55+
module.exports = { MyClass, Student };

lab1/main_test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const test = require('node:test');
2+
const assert = require('assert');
3+
const { MyClass, Student } = require('./main');
4+
5+
test("Test MyClass's addStudent", () => {
6+
// TODO
7+
throw new Error("Test not implemented");
8+
});
9+
10+
test("Test MyClass's getStudentById", () => {
11+
// TODO
12+
throw new Error("Test not implemented");
13+
});
14+
15+
test("Test Student's setName", () => {
16+
// TODO
17+
throw new Error("Test not implemented");
18+
});
19+
20+
test("Test Student's getName", () => {
21+
// TODO
22+
throw new Error("Test not implemented");
23+
});

lab1/validate.sh

Lines changed: 38 additions & 0 deletions
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 lab1-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%, should be 100%."
28+
exit 1
29+
else
30+
echo "[V] Coverage is 100%, great job!"
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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/bash
2+
3+
# Check if a file path was provided
4+
if [ -z "$1" ]
5+
then
6+
echo "Please provide a file path as an argument."
7+
exit 1
8+
fi
9+
10+
# Check if the file exists
11+
if [ ! -f "$1" ]
12+
then
13+
echo "File not found!"
14+
exit 1
15+
fi
16+
17+
# Read the file line by line
18+
while IFS= read -r line
19+
do
20+
# Create a new branch from main for each line in the file
21+
git checkout main
22+
git checkout -b "$line"
23+
done < "$1"

scripts/rebase-all.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/bash
2+
3+
git fetch origin
4+
5+
for branch in $(git branch -r | grep -v HEAD); do
6+
# Remove the "origin/" prefix
7+
branch=${branch#origin/}
8+
9+
if [[ "$branch" != "main" ]]; then
10+
git checkout "$branch"
11+
if [[ $? -ne 0 ]]; then
12+
echo "Checkout failed for branch $branch"
13+
exit 1
14+
fi
15+
git pull origin "$branch"
16+
if [[ $? -ne 0 ]]; then
17+
echo "Pull failed for branch $branch"
18+
exit 1
19+
fi
20+
git rebase main
21+
if [[ $? -ne 0 ]]; then
22+
echo "Rebase failed for branch $branch"
23+
exit 1
24+
fi
25+
git push origin "$branch"
26+
fi
27+
done
28+
29+
git checkout main

0 commit comments

Comments
 (0)