From 95a447de9c1c7e9b5dc6f560d41ad2a92ed799c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20H=C3=B6rmann?= Date: Sun, 21 Jul 2019 17:57:34 +0200 Subject: [PATCH] Tests for replacer (#2675) * Tests for Replacer: Replacer.Set and Replacer.Delete * update replacer test to new implementation * fix replacer: counted position wrong if placeholder was found * fix replacer: found placeholder again, if it was a non-existing one * test with spaces between the placeholders as this could have a different behaviour * Tests for Replacer.Map * Tests for Replacer.Set: check also for something like {l{test1} This should be replaced as {lTEST1REPLACEMENT * fix replacer: fix multiple occurrence of phOpen sign * Tests for Replacer: rewrite Set and ReplaceAll tests to use implementation not interface * Tests for Replacer: rewrite Delete test to use implementation not interface * Tests for Replacer: rewrite Map tests to use implementation not interface * Tests for Replacer: add test for NewReplacer * Tests for Replacer: add test for default replacements * Tests for Replacer: fixed and refactored tests * Tests for Replacer: moved default replacement tests to New-test as new should return a replace with provider which defines global replacements --- replacer.go | 5 +- replacer_test.go | 269 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 replacer_test.go diff --git a/replacer.go b/replacer.go index e8a4f9b13..d7beeab69 100644 --- a/replacer.go +++ b/replacer.go @@ -115,12 +115,13 @@ func (r *replacer) ReplaceAll(input, empty string) string { } } if !found { + lastWriteCursor = i continue } // advance cursor to end of placeholder - i = end + 1 - lastWriteCursor = i + i = end + lastWriteCursor = i + 1 } // flush any unwritten remainder diff --git a/replacer_test.go b/replacer_test.go new file mode 100644 index 000000000..a30a75d1c --- /dev/null +++ b/replacer_test.go @@ -0,0 +1,269 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package caddy + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "testing" +) + +func fakeReplacer() replacer { + return replacer{ + providers: make([]ReplacementFunc, 0), + static: make(map[string]string), + } +} + +// Tests the Set method by setting some variables and check if they are added to static +func TestReplacerSet(t *testing.T) { + rep := fakeReplacer() + + for _, tc := range []struct { + variable string + value string + }{ + { + variable: "test1", + value: "val1", + }, + { + variable: "asdf", + value: "123", + }, + { + variable: "äöü", + value: "öö_äü", + }, + { + variable: "with space", + value: "space value", + }, + { + variable: "1", + value: "test-123", + }, + { + variable: "mySuper_IP", + value: "1.2.3.4", + }, + { + variable: "testEmpty", + value: "", + }, + } { + rep.Set(tc.variable, tc.value) + + // test if key is added + if val, ok := rep.static[tc.variable]; ok { + if val != tc.value { + t.Errorf("Expected value '%s' for key '%s' got '%s'", tc.value, tc.variable, val) + } + } else { + t.Errorf("Expected existing key '%s' found nothing", tc.variable) + } + } + + // test if all keys are still there (by length) + length := len(rep.static) + if len(rep.static) != 7 { + t.Errorf("Expected length '%v' got '%v'", 7, length) + } +} + +func TestReplacerReplaceAll(t *testing.T) { + rep := replacer{ + providers: []ReplacementFunc{ + // split our possible vars to two functions (to test if both functions are called) + func(key string) (val string, ok bool) { + switch key { + case "test1": + return "val1", true + case "asdf": + return "123", true + case "äöü": + return "öö_äü", true + case "with space": + return "space value", true + default: + return "NOOO", false + } + }, + func(key string) (val string, ok bool) { + switch key { + case "1": + return "test-123", true + case "mySuper_IP": + return "1.2.3.4", true + case "testEmpty": + return "", true + default: + return "NOOO", false + } + }, + }, + } + + for _, tc := range []struct { + testInput string + expected string + }{ + { + // test vars without space + testInput: "{test1}{asdf}{äöü}{1}{with space}{mySuper_IP}", + expected: "val1123öö_äütest-123space value1.2.3.4", + }, + { + // test vars with space + testInput: "{test1} {asdf} {äöü} {1} {with space} {mySuper_IP} ", + expected: "val1 123 öö_äü test-123 space value 1.2.3.4 ", + }, + { + // test with empty val + testInput: "{test1} {testEmpty} {asdf} {1} ", + expected: "val1 EMPTY 123 test-123 ", + }, + { + // test vars with not finished placeholders + testInput: "{te{test1}{as{{df{1}", + expected: "{teval1{as{{dftest-123", + }, + { + // test with non existing vars + testInput: "{test1} {nope} {1} ", + expected: "val1 {nope} test-123 ", + }, + } { + actual := rep.ReplaceAll(tc.testInput, "EMPTY") + + // test if all are replaced as expected + if actual != tc.expected { + t.Errorf("Expected '%s' got '%s' for '%s'", tc.expected, actual, tc.testInput) + } + } +} + +func TestReplacerDelete(t *testing.T) { + rep := replacer{ + static: map[string]string{ + "key1": "val1", + "key2": "val2", + "key3": "val3", + "key4": "val4", + }, + } + + startLen := len(rep.static) + + toDel := []string{ + "key2", "key4", + } + + for _, key := range toDel { + rep.Delete(key) + + // test if key is removed from static map + if _, ok := rep.static[key]; ok { + t.Errorf("Expected '%s' to be removed. It is still in static map.", key) + } + } + + // check if static slice is smaller + expected := startLen - len(toDel) + actual := len(rep.static) + if len(rep.static) != expected { + t.Errorf("Expected length '%v' got length '%v'", expected, actual) + } +} + +func TestReplacerMap(t *testing.T) { + rep := fakeReplacer() + + for i, tc := range []ReplacementFunc{ + func(key string) (val string, ok bool) { + return "", false + }, + func(key string) (val string, ok bool) { + return "", false + }, + } { + rep.Map(tc) + + // test if function (which listens on specific key) is added by checking length + if len(rep.providers) == i+1 { + // check if the last function is the one we just added + pTc := fmt.Sprintf("%p", tc) + pRep := fmt.Sprintf("%p", rep.providers[i]) + if pRep != pTc { + t.Errorf("Expected func pointer '%s' got '%s'", pTc, pRep) + } + } else { + t.Errorf("Expected providers length '%v' got length '%v'", i+1, len(rep.providers)) + } + } +} + +func TestReplacerNew(t *testing.T) { + var tc = NewReplacer() + + rep, ok := tc.(*replacer) + if ok { + if len(rep.providers) != 2 { + t.Errorf("Expected providers length '%v' got length '%v'", 2, len(rep.providers)) + } else { + // test if default global replacements are added as the first provider + hostname, _ := os.Hostname() + os.Setenv("CADDY_REPLACER_TEST", "envtest") + + for _, tc := range []struct { + variable string + value string + }{ + { + variable: "system.hostname", + value: hostname, + }, + { + variable: "system.slash", + value: string(filepath.Separator), + }, + { + variable: "system.os", + value: runtime.GOOS, + }, + { + variable: "system.arch", + value: runtime.GOARCH, + }, + { + variable: "env.CADDY_REPLACER_TEST", + value: "envtest", + }, + } { + if val, ok := rep.providers[0](tc.variable); ok { + if val != tc.value { + t.Errorf("Expected value '%s' for key '%s' got '%s'", tc.value, tc.variable, val) + } + } else { + t.Errorf("Expected key '%s' to be recognized by first provider", tc.variable) + } + } + } + } else { + t.Errorf("Expected type of replacer %T got %T ", &replacer{}, tc) + } +}