package hw3test import ( "flag" "fmt" "os" "path/filepath" "testing" "text/template" "github.com/stretchr/testify/require" "go.uber.org/zap" ) var ( useDocker bool ) func init() { flag.BoolVar(&useDocker, "docker", false, "Run solution in Docker") } func TestHW(tt *testing.T) { if !flag.Parsed() { flag.Parse() } t := NewTestContext(tt) Info(t, "Starting tests") // loading commandline args template from disk, to run solution with it launchTmpl := os.Getenv("LAUNCH_TMPL") if launchTmpl == "" { launchTmpl = "./launch.tmpl" if useDocker { launchTmpl = "./launch_docker.tmpl" } } tmplContent, err := os.ReadFile(launchTmpl) require.NoError(t, err, "failed to read template file") launchTemplate := template.Must(template.New("launch").Parse(string(tmplContent))) runner := NewCmdRunner(launchTemplate, useDocker) workdir, err := os.Getwd() require.NoError(t, err, "failed to get current directory") tmpRoot := filepath.Join(workdir, "tmp") require.NoError(t, os.MkdirAll(tmpRoot, 0777), "failed to create tmp dir") workspace, err := filepath.EvalSymlinks(workdir) require.NoError(t, err, "failed to resolve test directory") tmpRoot, err = filepath.EvalSymlinks(tmpRoot) require.NoError(t, err, "failed to resolve tmp dir") rel, err := filepath.Rel(workspace, tmpRoot) require.NoError(t, err, "failed to verify tmp dir") require.Equal(t, "tmp", rel, "tmp dir must stay inside the test directory") tmpDir, err := os.MkdirTemp(tmpRoot, "run-") require.NoError(t, err, "failed to create run-specific tmp dir") require.NoError(t, os.Chmod(tmpDir, 0755), "failed to make tmp dir accessible to the server") t.Cleanup(func() { resolved, err := filepath.EvalSymlinks(tmpDir) if os.IsNotExist(err) { return } require.NoError(t, err, "failed to resolve run-specific tmp dir") rel, err := filepath.Rel(tmpRoot, resolved) require.NoError(t, err, "failed to verify run-specific tmp dir") require.Equal(t, filepath.Base(tmpDir), rel, "run-specific tmp dir must stay inside tmp dir") require.NoError(t, os.RemoveAll(tmpDir), "failed to remove run-specific tmp dir") }) score := 0 type RunResult struct { Name string Scored int Max int } runResults := []RunResult{} runGroup := func(name string, points int, f func(t *TC)) { t.RunByName(name, func(t *TC) { Info(t, "Starting tests group", zap.Int("points", points)) t.Cleanup(func() { r := RunResult{ Name: name, Max: points, } ok := !t.Failed() if ok { score += points r.Scored = points Info(t, "Tests group passed", zap.String("name", name), zap.Int("score", score)) } else { Warn(t, "Tests group failed", zap.String("name", name), zap.Int("score", score)) } runResults = append(runResults, r) }) f(t) }) } textEnv := &EnvGen{ MaxDepth: 1, MaxDirs: 3, MaxFiles: 5, TextOnly: true, MaxFileSizeKB: 64, TempDirectory: tmpDir, FilenameGen: SimpleFilenameGenerator(8), AllowEnv: false, } binaryEnv := &EnvGen{ MaxDepth: 4, MaxDirs: 16, MaxFiles: 25, TextOnly: false, MaxFileSizeKB: 1024, TempDirectory: tmpDir, FilenameGen: SimpleFilenameGenerator(16), AllowEnv: true, } largeEnv := &EnvGen{ MaxDepth: 1, MaxDirs: 6, MaxFiles: 6, TextOnly: false, MaxFileSizeKB: 192 * 1024, // 192 MB TempDirectory: tmpDir, SparseUnusedFiles: true, FilenameGen: SimpleFilenameGenerator(16), AllowEnv: true, } // Simple GET queries for existing text files, 3 points. runGroup("G1", 3, func(t *TC) { RunContractTests(t, runner, tmpDir, "G1") RunTestEmptyWorkDir(t, 42, runner) env := textEnv queries := &QueriesGen{ Count: 20, GetFile: true, GetFileNoErrors: true, GetDirectory: false, Post: false, Put: false, Delete: false, Compression: false, AllHeaders: false, } RunTests(t, 1337, runner, env, queries) RunTests(t, 1338, runner, env, queries) RunTests(t, 1339, runner, env, queries) }) // Simple GET queries for existing binary files, 1 point. runGroup("G2", 1, func(t *TC) { RunContractTests(t, runner, tmpDir, "G2") env := binaryEnv queries := &QueriesGen{ Count: 40, GetFile: true, GetFileNoErrors: true, GetDirectory: false, Post: false, Put: false, Delete: false, Compression: false, AllHeaders: false, } RunTests(t, 93, runner, env, queries) RunTests(t, 2945, runner, env, queries) RunTests(t, 3110, runner, env, queries) }) // Any GET queries, 1 point. runGroup("G3", 1, func(t *TC) { RunContractTests(t, runner, tmpDir, "G3") env := textEnv queries := &QueriesGen{ Count: 30, GetFile: true, GetFileNoErrors: false, GetDirectory: true, Post: false, Put: false, Delete: false, Compression: false, AllHeaders: false, } RunTests(t, 5311, runner, env, queries) RunTests(t, 2863, runner, env, queries) RunTests(t, 6712, runner, env, queries) RunTests(t, 7233, runner, env, queries) RunTests(t, 7067, runner, env, queries) RunTests(t, 3930, runner, env, queries) }) // Simple file server, 2 points. runGroup("G4", 2, func(t *TC) { RunContractTests(t, runner, tmpDir, "G4") env := binaryEnv queries := &QueriesGen{ Count: 30, GetFile: true, GetDirectory: true, Post: true, Put: true, Delete: true, Compression: false, AllHeaders: false, } RunTests(t, 3152, runner, env, queries) RunTests(t, 2929, runner, env, queries) RunTests(t, 6554, runner, env, queries) RunTests(t, 1388, runner, env, queries) RunTests(t, 1672, runner, env, queries) RunTests(t, 1769, runner, env, queries) }) // Extra headers, 1 point. runGroup("G5", 1, func(t *TC) { RunContractTests(t, runner, tmpDir, "G5") env := binaryEnv queries := &QueriesGen{ Count: 30, GetFile: true, GetDirectory: true, Post: true, Put: true, Delete: true, Compression: false, AllHeaders: true, } RunTests(t, 7942, runner, env, queries) RunTests(t, 1479, runner, env, queries) RunTests(t, 3324, runner, env, queries) RunTests(t, 6519, runner, env, queries) RunTests(t, 3746, runner, env, queries) RunTests(t, 1961, runner, env, queries) }) // Large files, 1 point. runGroup("G6", 1, func(t *TC) { RunContractTests(t, runner, tmpDir, "G6") env := largeEnv queries := &QueriesGen{ Count: 25, GetFile: true, GetDirectory: true, LargePuts: &LargePutPolicy{ RunSeed: 3793, LargeSizes: []int64{160 << 20, 191 << 20}, OtherMaxBytes: 8 << 20, }, Post: true, Put: true, Delete: true, Compression: false, AllHeaders: false, } RunTests(t, 7824, runner, env, queries) RunTests(t, 1671, runner, env, queries) RunTests(t, 3793, runner, env, queries) RunTests(t, 272, runner, env, queries) RunTests(t, 2715, runner, env, queries) RunTests(t, 1436, runner, env, queries) }) // Large files and gzip, 1 point. runGroup("G7", 1, func(t *TC) { RunContractTests(t, runner, tmpDir, "G7") env := largeEnv queries := &QueriesGen{ Count: 30, GetFile: true, GetDirectory: true, LargePuts: &LargePutPolicy{ RunSeed: 3224, LargeSizes: []int64{160 << 20, 191 << 20}, OtherMaxBytes: 8 << 20, }, Post: true, Put: true, Delete: true, Compression: true, AllHeaders: true, } RunTests(t, 3224, runner, env, queries) RunTests(t, 7507, runner, env, queries) RunTests(t, 4172, runner, env, queries) RunTests(t, 7777, runner, env, queries) RunTests(t, 6666, runner, env, queries) RunTests(t, 6094, runner, env, queries) RunTests(t, 6442, runner, env, queries) }) for _, r := range runResults { Info(t, fmt.Sprintf("Score for group [%s]: %d / %d", r.Name, r.Scored, r.Max)) } Info(t, "Tests finished", zap.Int("score", score)) fmt.Println("==================================================================================") fmt.Printf("SCORE: %d\n", score) fmt.Println("==================================================================================") }