From e2b54c384c6e2b7b7a5ab965d63d44ff5cfc1858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bertalan=20Kov=C3=A1cs?= Date: Tue, 16 Dec 2025 00:03:57 +0100 Subject: [PATCH 1/6] feat: init structure --- 25-testing/01-testify/.README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 25-testing/01-testify/.README.md diff --git a/25-testing/01-testify/.README.md b/25-testing/01-testify/.README.md new file mode 100644 index 0000000..e69de29 From c0ca3a6dab0ac67829af96e8dd6213e4cdee6518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bertalan=20Kov=C3=A1cs?= Date: Tue, 16 Dec 2025 00:08:06 +0100 Subject: [PATCH 2/6] feat: created exercise --- 25-testing/01-testify/.exercise.go | 82 +++++++++++++++++++++++++ 25-testing/01-testify/.exercise_test.go | 80 ++++++++++++++++++++++++ 25-testing/01-testify/exercise.yaml | 5 ++ 3 files changed, 167 insertions(+) create mode 100644 25-testing/01-testify/.exercise.go create mode 100644 25-testing/01-testify/.exercise_test.go create mode 100644 25-testing/01-testify/exercise.yaml diff --git a/25-testing/01-testify/.exercise.go b/25-testing/01-testify/.exercise.go new file mode 100644 index 0000000..6938dfe --- /dev/null +++ b/25-testing/01-testify/.exercise.go @@ -0,0 +1,82 @@ +package testimony + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// DO NOT REMOVE THIS COMMENT +//go:generate go run ../../exercises-cli.go -student-id=$STUDENT_ID generate +{{if eq (index . "name") "strstr"}} +func strstr(str string, substr string) int { + if substr == "" { + return 0 + } + + for i := 0; i <= len(str)-len(substr); i++ { + if str[i:i+len(substr)] == substr { + return i + } + } + + return -1 +} +{{end}} +{{if eq (index . "name") "strncat"}} +func strncat(dest *string, src string, n int) { + if n > len(src) { + n = len(src) + } + *dest += src[:n] +} +{{end}} +{{if eq (index . "name") "strncpy"}} +func strncpy(dest *string, src string, n int) { + if n > len(src) { + n = len(src) + } + *dest = src[:n] +} +{{end}} + +// INSERT YOUR CODE HERE +{{if eq (index . "name") "strstr"}} +func StrStrMatch(t *testing.T) { + +} + +func StrStrNoMatch(t *testing.T) { + +} + +func StrStrEmptySubString(t *testing.T) { + +} +{{end}} +{{if eq (index . "name") "strncat"}} +func StrNCatInbounds(t *testing.T) { + +} + +func StrNCatOutOfBounds(t *testing.T) { + +} + +func StrNCatEmptySource(t *testing.T) { + +} +{{end}} +{{if eq (index . "name") "strncpy"}} +func StrNCpyINbounds(t *testing.T) { + +} + +func StrNCpyEmptyDestination(t *testing.T) { + +} + +func StrNCpyEmptySource(t *testing.T) { + +} +{{end}} \ No newline at end of file diff --git a/25-testing/01-testify/.exercise_test.go b/25-testing/01-testify/.exercise_test.go new file mode 100644 index 0000000..8300b8b --- /dev/null +++ b/25-testing/01-testify/.exercise_test.go @@ -0,0 +1,80 @@ +package testimony + +import ( + "os" + "regexp" + "testing" +) + +const requiredAsserts = 3 + +func TestStudentUsesAssertions(t *testing.T) { + src, err := os.ReadFile("exercise.go") + if err != nil { + t.Fatalf("cannot read exercise.go: %v", err) + } + + source := string(src) + + re := regexp.MustCompile(`assert\.\w+\s*\(`) + if !re.MatchString(source) { + t.Fatalf("no assert.* call found in exercise.go") + } +} + +func TestStudentHasEnoughAsserts(t *testing.T) { + src, err := os.ReadFile("exercise.go") + if err != nil { + t.Fatalf("cannot read exercise.go: %v", err) + } + + source := string(src) + + re := regexp.MustCompile(`assert\.\w+\s*\(`) + matches := re.FindAllString(source, -1) + + if len(matches) < requiredAsserts { + t.Fatalf("not enough assert calls: found %d, required %d", len(matches), requiredAsserts) + } +} + +func TestNoTrivialAssertions(t *testing.T) { + src, err := os.ReadFile("exercise.go") + if err != nil { + t.Fatalf("cannot read exercise.go: %v", err) + } + + source := string(src) + + patterns := []string{ + `assert\.False\s*\(\s*t\s*,\s*false\s*[,)]`, + `assert\.True\s*\(\s*t\s*,\s*true\s*[,)]`, + `assert\.Equal\s*\(\s*t\s*,\s*true\s*,\s*true\s*[,)]`, + `assert\.Equal\s*\(\s*t\s*,\s*false\s*,\s*false\s*[,)]`, + } + + for _, p := range patterns { + re := regexp.MustCompile(p) + if re.MatchString(source) { + t.Fatalf("trivial assertion detected") + } + } +} + +func TestRunStudentTests(t *testing.T) { +{{if eq (index . "name") "strstr"}} + t.Run("StrStrMatch", StrStrMatch) + t.Run("StrStrNoMatch", StrStrNoMatch) + t.Run("StrStrEmptySubString", StrStrEmptySubString) +{{end}} +{{if eq (index . "name") "strncat"}} + t.Run("StrNCatInbounds", StrNCatInbounds) + t.Run("StrNCatOutOfBounds", StrNCatOutOfBounds) + t.Run("StrNCatEmptySource", StrNCatEmptySource) +{{end}} +{{if eq (index . "name") "strncpy"}} + t.Run("StrNCpyINbounds", StrNCpyINbounds) + t.Run("StrNCpyEmptyDestination", StrNCpyEmptyDestination) + t.Run("StrNCpyEmptySource", StrNCpyEmptySource) +{{end}} +} diff --git a/25-testing/01-testify/exercise.yaml b/25-testing/01-testify/exercise.yaml new file mode 100644 index 0000000..39bf2b0 --- /dev/null +++ b/25-testing/01-testify/exercise.yaml @@ -0,0 +1,5 @@ +name: testimony +input: +- name: strstr +- name: strncat +- name: strncpy \ No newline at end of file From 61141181d1e5d29aafd9b73eab3384ac78904eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bertalan=20Kov=C3=A1cs?= Date: Tue, 16 Dec 2025 00:29:40 +0100 Subject: [PATCH 3/6] feat: .README.md --- 25-testing/01-testify/.README.md | 85 ++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/25-testing/01-testify/.README.md b/25-testing/01-testify/.README.md index e69de29..6aaa6b6 100644 --- a/25-testing/01-testify/.README.md +++ b/25-testing/01-testify/.README.md @@ -0,0 +1,85 @@ +# Testing + +In this task, you need to test the function `{{ index . "name" }}` in Go using **`testify/assert`**. + +## Function Description + +{{- if eq (index . "name") "strstr" -}} +### `strstr(str string, substr string) int` + +- Returns the **index of the first occurrence** of `substr` in `str`. +- Returns `0` if `substr` is empty. +- Returns `-1` if `substr` is not found. + +**Example:** + +```go +strstr("hello world", "lo") // 3 + +strstr("hello", "Go") // -1 + +strstr("abc", "") // 0 +``` +{{- end -}} +{{- if eq (index . "name") "strncat" -}} +### `strncat(dest *string, src string, n int)` + +- Appends at most `n` characters from `src` to `dest`. +- Modifies `dest` in place + +**Example:** + +```go +dest := "Hello" +strncat(&dest, "World", 3) // dest == "HelloWor" + +dest := "Hi" +strncat(&dest, "All", 10) // dest == "HiAll" + +dest := "" +strncat(&dest, "Go", 1) // dest == "G" +``` +{{- end -}} +{{- if eq (index . "name") "strncpy" -}} +### `strncpy(dest *string, src string, n int)` + +- Copies at most `n` characters from `src` to `dest`. +- Modifies `dest` in place + +**Example:** + +```go +dest := "Hello" +strncpy(&dest, "Go", 2) // dest == "Go" + +dest := "" +strncpy(&dest, "World", 3) // dest == "Wor" + +dest := "Hi" +strncpy(&dest, "", 1) // dest == "" +``` +{{- end -}} + +## Requirements +1. Use only assert statements from the [testify/assert](https://pkg.go.dev/github.com/stretchr/testify/assert) package. +2. Do not modify the provided function signature. +3. Write exactly three test functions for the function `{{ index . "name" }}`. + +## Needed test functions: +{{- if eq (index . "name") "strstr" -}} +- `StrStrMatch` — test when the substring exists +- `StrStrNoMatch` — test when the substring does not exist +- `StrStrEmptySubString` — test when the substring is empty +{{end}} +{{- if eq (index . "name") "strncat" -}} +- `StrNCatInbounds` — append a part of the source within index bounds +- `StrNCatOutOfBounds` — append more than the source length +- `StrNCatEmptySource` — append from an empty source +{{end}} +{{- if eq (index . "name") "strncpy" -}} +- `StrNCpyINbounds` — copy part of the source within index bounds +- `StrNCpyEmptyDestination` — copy into an empty destination +- `StrNCpyEmptySource` — copy from an empty source +{{end}} + +Insert the code into the file `exercise.go` at the placeholder `// INSERT YOUR CODE HERE`. \ No newline at end of file From d9dcece3a4c62f8f45b420cddf30a2ffe38be5f2 Mon Sep 17 00:00:00 2001 From: Tamas Levai Date: Sat, 20 Dec 2025 11:45:53 +0100 Subject: [PATCH 4/6] fix(25-testing/01): Add missing exercise.go --- 25-testing/01-testify/exercise.go | 4 ++++ Makefile | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 25-testing/01-testify/exercise.go diff --git a/25-testing/01-testify/exercise.go b/25-testing/01-testify/exercise.go new file mode 100644 index 0000000..824f993 --- /dev/null +++ b/25-testing/01-testify/exercise.go @@ -0,0 +1,4 @@ +package testimony + +// DO NOT REMOVE THIS COMMENT +//go:generate go run ../../exercises-cli.go -student-id=$STUDENT_ID generate diff --git a/Makefile b/Makefile index a11c9d1..6b79566 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,8 @@ EXERCISE_DIRS=\ 21-channels \ 22-context \ 23-misc \ - 24-generics + 24-generics \ + 25-testing # LAB subdirs LAB_DIRS=\ From a087201a051035a9c2a6590a6c4228c178e8564c Mon Sep 17 00:00:00 2001 From: Tamas Levai Date: Sat, 20 Dec 2025 12:12:08 +0100 Subject: [PATCH 5/6] fix(25-testing/01): Remove extra empty lines --- 25-testing/01-testify/.README.md | 47 +++++++++++++++++------------- 25-testing/01-testify/.exercise.go | 24 ++++++++------- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/25-testing/01-testify/.README.md b/25-testing/01-testify/.README.md index 6aaa6b6..146a547 100644 --- a/25-testing/01-testify/.README.md +++ b/25-testing/01-testify/.README.md @@ -1,15 +1,16 @@ # Testing -In this task, you need to test the function `{{ index . "name" }}` in Go using **`testify/assert`**. +In this task, you need to test the function `{{ index . "name" }}` in Go using **`testify/assert`**. ## Function Description -{{- if eq (index . "name") "strstr" -}} +{{- if eq (index . "name") "strstr" }} + ### `strstr(str string, substr string) int` -- Returns the **index of the first occurrence** of `substr` in `str`. -- Returns `0` if `substr` is empty. -- Returns `-1` if `substr` is not found. +- Returns the **index of the first occurrence** of `substr` in `str`. +- Returns `0` if `substr` is empty. +- Returns `-1` if `substr` is not found. **Example:** @@ -20,11 +21,12 @@ strstr("hello", "Go") // -1 strstr("abc", "") // 0 ``` -{{- end -}} -{{- if eq (index . "name") "strncat" -}} +{{- end }} +{{- if eq (index . "name") "strncat" }} + ### `strncat(dest *string, src string, n int)` -- Appends at most `n` characters from `src` to `dest`. +- Appends at most `n` characters from `src` to `dest`. - Modifies `dest` in place **Example:** @@ -39,12 +41,13 @@ strncat(&dest, "All", 10) // dest == "HiAll" dest := "" strncat(&dest, "Go", 1) // dest == "G" ``` -{{- end -}} -{{- if eq (index . "name") "strncpy" -}} +{{- end }} +{{- if eq (index . "name") "strncpy" }} + ### `strncpy(dest *string, src string, n int)` -- Copies at most `n` characters from `src` to `dest`. -- Modifies `dest` in place +- Copies at most `n` characters from `src` to `dest`. +- Modifies `dest` in place **Example:** @@ -58,7 +61,7 @@ strncpy(&dest, "World", 3) // dest == "Wor" dest := "Hi" strncpy(&dest, "", 1) // dest == "" ``` -{{- end -}} +{{- end }} ## Requirements 1. Use only assert statements from the [testify/assert](https://pkg.go.dev/github.com/stretchr/testify/assert) package. @@ -66,20 +69,24 @@ strncpy(&dest, "", 1) // dest == "" 3. Write exactly three test functions for the function `{{ index . "name" }}`. ## Needed test functions: -{{- if eq (index . "name") "strstr" -}} + +{{- if eq (index . "name") "strstr" }} + - `StrStrMatch` — test when the substring exists - `StrStrNoMatch` — test when the substring does not exist - `StrStrEmptySubString` — test when the substring is empty -{{end}} -{{- if eq (index . "name") "strncat" -}} +{{- end }} +{{- if eq (index . "name") "strncat" }} + - `StrNCatInbounds` — append a part of the source within index bounds - `StrNCatOutOfBounds` — append more than the source length - `StrNCatEmptySource` — append from an empty source -{{end}} -{{- if eq (index . "name") "strncpy" -}} +{{- end }} +{{- if eq (index . "name") "strncpy" }} + - `StrNCpyINbounds` — copy part of the source within index bounds - `StrNCpyEmptyDestination` — copy into an empty destination - `StrNCpyEmptySource` — copy from an empty source -{{end}} +{{- end }} -Insert the code into the file `exercise.go` at the placeholder `// INSERT YOUR CODE HERE`. \ No newline at end of file +Insert the code into the file `exercise.go` at the placeholder `// INSERT YOUR CODE HERE`. diff --git a/25-testing/01-testify/.exercise.go b/25-testing/01-testify/.exercise.go index 6938dfe..a7ea08d 100644 --- a/25-testing/01-testify/.exercise.go +++ b/25-testing/01-testify/.exercise.go @@ -8,7 +8,8 @@ import ( // DO NOT REMOVE THIS COMMENT //go:generate go run ../../exercises-cli.go -student-id=$STUDENT_ID generate -{{if eq (index . "name") "strstr"}} +{{- if eq (index . "name") "strstr"}} + func strstr(str string, substr string) int { if substr == "" { return 0 @@ -22,26 +23,29 @@ func strstr(str string, substr string) int { return -1 } -{{end}} -{{if eq (index . "name") "strncat"}} +{{- end}} +{{- if eq (index . "name") "strncat"}} + func strncat(dest *string, src string, n int) { if n > len(src) { n = len(src) } *dest += src[:n] } -{{end}} -{{if eq (index . "name") "strncpy"}} +{{- end}} +{{- if eq (index . "name") "strncpy"}} + func strncpy(dest *string, src string, n int) { if n > len(src) { n = len(src) } *dest = src[:n] } -{{end}} +{{- end}} + // INSERT YOUR CODE HERE -{{if eq (index . "name") "strstr"}} +{{- if eq (index . "name") "strstr"}} func StrStrMatch(t *testing.T) { } @@ -54,7 +58,7 @@ func StrStrEmptySubString(t *testing.T) { } {{end}} -{{if eq (index . "name") "strncat"}} +{{- if eq (index . "name") "strncat"}} func StrNCatInbounds(t *testing.T) { } @@ -67,7 +71,7 @@ func StrNCatEmptySource(t *testing.T) { } {{end}} -{{if eq (index . "name") "strncpy"}} +{{- if eq (index . "name") "strncpy"}} func StrNCpyINbounds(t *testing.T) { } @@ -79,4 +83,4 @@ func StrNCpyEmptyDestination(t *testing.T) { func StrNCpyEmptySource(t *testing.T) { } -{{end}} \ No newline at end of file +{{end}} From a09931b249166bae46b27f6cb959f670be40f381 Mon Sep 17 00:00:00 2001 From: Tamas Levai Date: Sat, 20 Dec 2025 12:20:06 +0100 Subject: [PATCH 6/6] feat(25-testing/1): Resolve test framework compatibility issues Rework example to make it fit into our automated test framework that relies on '.exercise.go' files too. --- 25-testing/01-testify/.README.md | 21 +++--- 25-testing/01-testify/.exercise.go | 86 ------------------------- 25-testing/01-testify/.exercise_test.go | 47 ++++++++++++-- 25-testing/01-testify/exercise.go | 9 +++ 4 files changed, 62 insertions(+), 101 deletions(-) delete mode 100644 25-testing/01-testify/.exercise.go diff --git a/25-testing/01-testify/.README.md b/25-testing/01-testify/.README.md index 146a547..b4ca601 100644 --- a/25-testing/01-testify/.README.md +++ b/25-testing/01-testify/.README.md @@ -4,6 +4,9 @@ In this task, you need to test the function `{{ index . "name" }}` in Go using * ## Function Description +The implementation is available in [exercise_test.go](exercise_test.go), if needed. +Below we show some examples of the expected behaviors. + {{- if eq (index . "name") "strstr" }} ### `strstr(str string, substr string) int` @@ -72,21 +75,21 @@ strncpy(&dest, "", 1) // dest == "" {{- if eq (index . "name") "strstr" }} -- `StrStrMatch` — test when the substring exists -- `StrStrNoMatch` — test when the substring does not exist -- `StrStrEmptySubString` — test when the substring is empty +- `StrStrMatch(t *testing.T)` — test when the substring exists +- `StrStrNoMatch(t *testing.T)` — test when the substring does not exist +- `StrStrEmptySubString(t *testing.T)` — test when the substring is empty {{- end }} {{- if eq (index . "name") "strncat" }} -- `StrNCatInbounds` — append a part of the source within index bounds -- `StrNCatOutOfBounds` — append more than the source length -- `StrNCatEmptySource` — append from an empty source +- `StrNCatInbounds(t *testing.T)` — append a part of the source within index bounds +- `StrNCatOutOfBounds(t *testing.T)` — append more than the source length +- `StrNCatEmptySource(t *testing.T)` — append from an empty source {{- end }} {{- if eq (index . "name") "strncpy" }} -- `StrNCpyINbounds` — copy part of the source within index bounds -- `StrNCpyEmptyDestination` — copy into an empty destination -- `StrNCpyEmptySource` — copy from an empty source +- `StrNCpyINbounds(t *testing.T)` — copy part of the source within index bounds +- `StrNCpyEmptyDestination(t *testing.T)` — copy into an empty destination +- `StrNCpyEmptySource(t *testing.T)` — copy from an empty source {{- end }} Insert the code into the file `exercise.go` at the placeholder `// INSERT YOUR CODE HERE`. diff --git a/25-testing/01-testify/.exercise.go b/25-testing/01-testify/.exercise.go deleted file mode 100644 index a7ea08d..0000000 --- a/25-testing/01-testify/.exercise.go +++ /dev/null @@ -1,86 +0,0 @@ -package testimony - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -// DO NOT REMOVE THIS COMMENT -//go:generate go run ../../exercises-cli.go -student-id=$STUDENT_ID generate -{{- if eq (index . "name") "strstr"}} - -func strstr(str string, substr string) int { - if substr == "" { - return 0 - } - - for i := 0; i <= len(str)-len(substr); i++ { - if str[i:i+len(substr)] == substr { - return i - } - } - - return -1 -} -{{- end}} -{{- if eq (index . "name") "strncat"}} - -func strncat(dest *string, src string, n int) { - if n > len(src) { - n = len(src) - } - *dest += src[:n] -} -{{- end}} -{{- if eq (index . "name") "strncpy"}} - -func strncpy(dest *string, src string, n int) { - if n > len(src) { - n = len(src) - } - *dest = src[:n] -} -{{- end}} - - -// INSERT YOUR CODE HERE -{{- if eq (index . "name") "strstr"}} -func StrStrMatch(t *testing.T) { - -} - -func StrStrNoMatch(t *testing.T) { - -} - -func StrStrEmptySubString(t *testing.T) { - -} -{{end}} -{{- if eq (index . "name") "strncat"}} -func StrNCatInbounds(t *testing.T) { - -} - -func StrNCatOutOfBounds(t *testing.T) { - -} - -func StrNCatEmptySource(t *testing.T) { - -} -{{end}} -{{- if eq (index . "name") "strncpy"}} -func StrNCpyINbounds(t *testing.T) { - -} - -func StrNCpyEmptyDestination(t *testing.T) { - -} - -func StrNCpyEmptySource(t *testing.T) { - -} -{{end}} diff --git a/25-testing/01-testify/.exercise_test.go b/25-testing/01-testify/.exercise_test.go index 8300b8b..271d328 100644 --- a/25-testing/01-testify/.exercise_test.go +++ b/25-testing/01-testify/.exercise_test.go @@ -61,20 +61,55 @@ func TestNoTrivialAssertions(t *testing.T) { } } +{{- if eq (index . "name") "strstr"}} + +func strstr(str string, substr string) int { + if substr == "" { + return 0 + } + + for i := 0; i <= len(str)-len(substr); i++ { + if str[i:i+len(substr)] == substr { + return i + } + } + + return -1 +} +{{- end}} +{{- if eq (index . "name") "strncat"}} + +func strncat(dest *string, src string, n int) { + if n > len(src) { + n = len(src) + } + *dest += src[:n] +} +{{- end}} +{{- if eq (index . "name") "strncpy"}} + +func strncpy(dest *string, src string, n int) { + if n > len(src) { + n = len(src) + } + *dest = src[:n] +} +{{- end}} + func TestRunStudentTests(t *testing.T) { -{{if eq (index . "name") "strstr"}} +{{- if eq (index . "name") "strstr"}} t.Run("StrStrMatch", StrStrMatch) t.Run("StrStrNoMatch", StrStrNoMatch) t.Run("StrStrEmptySubString", StrStrEmptySubString) -{{end}} -{{if eq (index . "name") "strncat"}} +{{- end}} +{{- if eq (index . "name") "strncat"}} t.Run("StrNCatInbounds", StrNCatInbounds) t.Run("StrNCatOutOfBounds", StrNCatOutOfBounds) t.Run("StrNCatEmptySource", StrNCatEmptySource) -{{end}} -{{if eq (index . "name") "strncpy"}} +{{- end}} +{{- if eq (index . "name") "strncpy"}} t.Run("StrNCpyINbounds", StrNCpyINbounds) t.Run("StrNCpyEmptyDestination", StrNCpyEmptyDestination) t.Run("StrNCpyEmptySource", StrNCpyEmptySource) -{{end}} +{{- end}} } diff --git a/25-testing/01-testify/exercise.go b/25-testing/01-testify/exercise.go index 824f993..ba32903 100644 --- a/25-testing/01-testify/exercise.go +++ b/25-testing/01-testify/exercise.go @@ -1,4 +1,13 @@ package testimony +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + // DO NOT REMOVE THIS COMMENT //go:generate go run ../../exercises-cli.go -student-id=$STUDENT_ID generate + + +// INSERT YOUR CODE HERE