diff --git a/25-testing/01-testify/.README.md b/25-testing/01-testify/.README.md new file mode 100644 index 0000000..b4ca601 --- /dev/null +++ b/25-testing/01-testify/.README.md @@ -0,0 +1,95 @@ +# Testing + +In this task, you need to test the function `{{ index . "name" }}` in Go using **`testify/assert`**. + +## 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` + +- 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(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(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(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_test.go b/25-testing/01-testify/.exercise_test.go new file mode 100644 index 0000000..271d328 --- /dev/null +++ b/25-testing/01-testify/.exercise_test.go @@ -0,0 +1,115 @@ +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") + } + } +} + +{{- 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"}} + 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.go b/25-testing/01-testify/exercise.go new file mode 100644 index 0000000..ba32903 --- /dev/null +++ b/25-testing/01-testify/exercise.go @@ -0,0 +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 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 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=\