Files
leanote/vendor/github.com/revel/config/all_test.go
2017-11-30 19:55:33 +08:00

475 lines
14 KiB
Go

// Copyright 2009 The "config" 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 config
import (
"bufio"
"os"
"reflect"
"strings"
"testing"
)
const (
tmpFilename = "testdata/__test.go"
sourceFilename = "testdata/source.cfg"
targetFilename = "testdata/target.cfg"
)
func testGet(t *testing.T, c *Config, section string, option string,
expected interface{}) {
ok := false
switch expected.(type) {
case string:
v, _ := c.String(section, option)
if v == expected.(string) {
ok = true
}
case int:
v, _ := c.Int(section, option)
if v == expected.(int) {
ok = true
}
case bool:
v, _ := c.Bool(section, option)
if v == expected.(bool) {
ok = true
}
default:
t.Fatalf("Bad test case")
}
if !ok {
v, _ := c.String(section, option)
t.Errorf("Get failure: expected different value for %s %s (expected: [%#v] got: [%#v])", section, option, expected, v)
}
}
// TestInMemory creates configuration representation and run multiple tests in-memory.
func TestInMemory(t *testing.T) {
c := NewDefault()
// == Test empty structure
// should be empty
if len(c.Sections()) != 1 {
t.Errorf("Sections failure: invalid length")
}
// test presence of missing section
if c.HasSection("no-section") {
t.Errorf("HasSection failure: invalid section")
}
// get options for missing section
_, err := c.Options("no-section")
if err == nil {
t.Errorf("Options failure: invalid section")
}
// test presence of option for missing section
if c.HasOption("no-section", "no-option") {
t.Errorf("HasSection failure: invalid/section/option")
}
// get value from missing section/option
_, err = c.String("no-section", "no-option")
if err == nil {
t.Errorf("String failure: got value for missing section/option")
}
// get value from missing section/option
_, err = c.Int("no-section", "no-option")
if err == nil {
t.Errorf("Int failure: got value for missing section/option")
}
// remove missing section
if c.RemoveSection("no-section") {
t.Errorf("RemoveSection failure: removed missing section")
}
// remove missing section/option
if c.RemoveOption("no-section", "no-option") {
t.Errorf("RemoveOption failure: removed missing section/option")
}
// == Fill up structure
// add section
if !c.AddSection("section1") {
t.Errorf("AddSection failure: false on first insert")
}
// re-add same section
if c.AddSection("section1") {
t.Errorf("AddSection failure: true on second insert")
}
// default section always exists
if c.AddSection(DefaultSection) {
t.Errorf("AddSection failure: true on default section insert")
}
// add option/value
if !c.AddOption("section1", "option1", "value1") {
t.Errorf("AddOption failure: false on first insert")
}
testGet(t, c, "section1", "option1", "value1") // read it back
// overwrite value
if c.AddOption("section1", "option1", "value2") {
t.Errorf("AddOption failure: true on second insert")
}
testGet(t, c, "section1", "option1", "value2") // read it back again
// remove option/value
if !c.RemoveOption("section1", "option1") {
t.Errorf("RemoveOption failure: false on first remove")
}
// remove again
if c.RemoveOption("section1", "option1") {
t.Errorf("RemoveOption failure: true on second remove")
}
// read it back again
_, err = c.String("section1", "option1")
if err == nil {
t.Errorf("String failure: got value for removed section/option")
}
// remove existing section
if !c.RemoveSection("section1") {
t.Errorf("RemoveSection failure: false on first remove")
}
// remove again
if c.RemoveSection("section1") {
t.Errorf("RemoveSection failure: true on second remove")
}
// == Test types
// add section
if !c.AddSection("section2") {
t.Errorf("AddSection failure: false on first insert")
}
// add number
if !c.AddOption("section2", "test-number", "666") {
t.Errorf("AddOption failure: false on first insert")
}
testGet(t, c, "section2", "test-number", 666) // read it back
// add 'yes' (bool)
if !c.AddOption("section2", "test-yes", "yes") {
t.Errorf("AddOption failure: false on first insert")
}
testGet(t, c, "section2", "test-yes", true) // read it back
// add 'false' (bool)
if !c.AddOption("section2", "test-false", "false") {
t.Errorf("AddOption failure: false on first insert")
}
testGet(t, c, "section2", "test-false", false) // read it back
// == Test cycle
c.AddOption(DefaultSection, "opt1", "%(opt2)s")
c.AddOption(DefaultSection, "opt2", "%(opt1)s")
_, err = c.String(DefaultSection, "opt1")
if err == nil {
t.Errorf("String failure: no error for cycle")
} else if !strings.Contains(err.Error(), "cycle") {
t.Errorf("String failure: incorrect error for cycle")
}
}
// TestReadFile creates a 'tough' configuration file and test (read) parsing.
func TestReadFile(t *testing.T) {
file, err := os.Create(tmpFilename)
if err != nil {
t.Fatal("Test cannot run because cannot write temporary file: " + tmpFilename)
}
err = os.Setenv("GO_CONFIGFILE_TEST_ENV_VAR", "configvalue12345")
if err != nil {
t.Fatalf("Test cannot run because cannot set environment variable GO_CONFIGFILE_TEST_ENV_VAR: %#v", err)
}
buf := bufio.NewWriter(file)
buf.WriteString("optionInDefaultSection=true\n")
buf.WriteString("[section-1]\n")
buf.WriteString("option1=value1 ; This is a comment\n")
buf.WriteString("option2 : 2#Not a comment\t#Now this is a comment after a TAB\n")
buf.WriteString(" # Let me put another comment\n")
buf.WriteString("option3= line1\n line2: \n\tline3=v # Comment multiline with := in value\n")
buf.WriteString("; Another comment\n")
buf.WriteString("[" + DefaultSection + "]\n")
buf.WriteString("variable1=small\n")
buf.WriteString("variable2=a_part_of_a_%(variable1)s_test\n")
buf.WriteString("[secTION-2]\n")
buf.WriteString("IS-flag-TRUE=Yes\n")
buf.WriteString("[section-1] # comment on section header\n") // continue again [section-1]
buf.WriteString("option4=this_is_%(variable2)s.\n")
buf.WriteString("envoption1=this_uses_${GO_CONFIGFILE_TEST_ENV_VAR}_env\n")
buf.WriteString("optionInDefaultSection=false")
buf.Flush()
file.Close()
c, err := ReadDefault(tmpFilename)
if err != nil {
t.Fatalf("ReadDefault failure: %s", err)
}
// check number of sections
if len(c.Sections()) != 3 {
t.Errorf("Sections failure: wrong number of sections")
}
// check number of options 6 of [section-1] plus 2 of [default]
opts, _ := c.Options("section-1")
if len(opts) != 8 {
t.Errorf("Options failure: wrong number of options: %d", len(opts))
}
testGet(t, c, "section-1", "option1", "value1")
testGet(t, c, "section-1", "option2", "2#Not a comment")
testGet(t, c, "section-1", "option3", "line1\nline2:\nline3=v")
testGet(t, c, "section-1", "option4", "this_is_a_part_of_a_small_test.")
testGet(t, c, "section-1", "envoption1", "this_uses_configvalue12345_env")
testGet(t, c, "section-1", "optionInDefaultSection", false)
testGet(t, c, "section-2", "optionInDefaultSection", true)
testGet(t, c, "secTION-2", "IS-flag-TRUE", true) // case-sensitive
}
// TestWriteReadFile tests writing and reading back a configuration file.
func TestWriteReadFile(t *testing.T) {
cw := NewDefault()
// write file; will test only read later on
cw.AddSection("First-Section")
cw.AddOption("First-Section", "option1", "value option1")
cw.AddOption("First-Section", "option2", "2")
cw.AddOption("", "host", "www.example.com")
cw.AddOption(DefaultSection, "protocol", "https://")
cw.AddOption(DefaultSection, "base-url", "%(protocol)s%(host)s")
cw.AddOption("Another-Section", "useHTTPS", "y")
cw.AddOption("Another-Section", "url", "%(base-url)s/some/path")
cw.WriteFile(tmpFilename, 0644, "Test file for test-case")
// read back file and test
cr, err := ReadDefault(tmpFilename)
if err != nil {
t.Fatalf("ReadDefault failure: %s", err)
}
testGet(t, cr, "First-Section", "option1", "value option1")
testGet(t, cr, "First-Section", "option2", 2)
testGet(t, cr, "Another-Section", "useHTTPS", true)
testGet(t, cr, "Another-Section", "url", "https://www.example.com/some/path")
defer os.Remove(tmpFilename)
}
// TestSectionOptions tests read options in a section without default options.
func TestSectionOptions(t *testing.T) {
cw := NewDefault()
// write file; will test only read later on
cw.AddSection("First-Section")
cw.AddOption("First-Section", "option1", "value option1")
cw.AddOption("First-Section", "option2", "2")
cw.AddOption("", "host", "www.example.com")
cw.AddOption(DefaultSection, "protocol", "https://")
cw.AddOption(DefaultSection, "base-url", "%(protocol)s%(host)s")
cw.AddOption("Another-Section", "useHTTPS", "y")
cw.AddOption("Another-Section", "url", "%(base-url)s/some/path")
cw.WriteFile(tmpFilename, 0644, "Test file for test-case")
// read back file and test
cr, err := ReadDefault(tmpFilename)
if err != nil {
t.Fatalf("ReadDefault failure: %s", err)
}
options, err := cr.SectionOptions("First-Section")
if err != nil {
t.Fatalf("SectionOptions failure: %s", err)
}
if len(options) != 2 {
t.Fatalf("SectionOptions reads wrong data: %v", options)
}
expected := map[string]bool{
"option1": true,
"option2": true,
}
actual := map[string]bool{}
for _, v := range options {
actual[v] = true
}
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("SectionOptions reads wrong data: %v", options)
}
options, err = cr.SectionOptions(DefaultSection)
if err != nil {
t.Fatalf("SectionOptions failure: %s", err)
}
expected = map[string]bool{
"host": true,
"protocol": true,
"base-url": true,
}
actual = map[string]bool{}
for _, v := range options {
actual[v] = true
}
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("SectionOptions reads wrong data: %v", options)
}
defer os.Remove(tmpFilename)
}
// TestMerge tests merging 2 configurations.
func TestMerge(t *testing.T) {
target, error := ReadDefault(targetFilename)
if error != nil {
t.Fatalf("Unable to read target config file '%s'", targetFilename)
}
source, error := ReadDefault(sourceFilename)
if error != nil {
t.Fatalf("Unable to read source config file '%s'", sourceFilename)
}
target.Merge(source)
// Assert whether a regular option was merged from source -> target
if result, _ := target.String(DefaultSection, "one"); result != "source1" {
t.Errorf("Expected 'one' to be '1' but instead it was '%s'", result)
}
// Assert that a non-existent option in source was not overwritten
if result, _ := target.String(DefaultSection, "five"); result != "5" {
t.Errorf("Expected 'five' to be '5' but instead it was '%s'", result)
}
// Assert that a folded option was correctly unfolded
if result, _ := target.String(DefaultSection, "two_+_three"); result != "source2 + source3" {
t.Errorf("Expected 'two_+_three' to be 'source2 + source3' but instead it was '%s'", result)
}
if result, _ := target.String(DefaultSection, "four"); result != "4" {
t.Errorf("Expected 'four' to be '4' but instead it was '%s'", result)
}
// Assert that a section option has been merged
if result, _ := target.String("X", "x.one"); result != "sourcex1" {
t.Errorf("Expected '[X] x.one' to be 'sourcex1' but instead it was '%s'", result)
}
if result, _ := target.String("X", "x.four"); result != "x4" {
t.Errorf("Expected '[X] x.four' to be 'x4' but instead it was '%s'", result)
}
}
func TestLoadContextOneConf(t *testing.T) {
ctx, err := LoadContext("app.conf", []string{"testdata/conf-path1"})
if err != nil {
t.Errorf("Error: %v", err)
t.FailNow()
}
ctx.SetSection("X")
result, found := ctx.String("x.three")
if !strings.EqualFold("conf1-sourcex3", result) {
t.Errorf("Expected '[X] x.three' to be 'conf1-sourcex3' but instead it was '%s'", result)
}
_, found = ctx.String("x.notexists")
if found {
t.Error("Config 'x.notexists' shouldn't found")
}
ctx.SetSection("Y")
result, found = ctx.String("y.one")
if !strings.EqualFold("conf1-sourcey1", result) {
t.Errorf("Expected '[Y] y.one' to be 'conf1-sourcey1' but instead it was '%s'", result)
}
_, found = ctx.String("y.notexists")
if found {
t.Error("Config 'y.notexists' shouldn't found")
}
}
func TestLoadContextMultipleConfWithPriority(t *testing.T) {
ctx, err := LoadContext("app.conf", []string{"testdata/conf-path1", "testdata/conf-path2"})
if err != nil {
t.Errorf("Error: %v", err)
t.FailNow()
}
ctx.SetSection("X")
result, found := ctx.String("x.two")
if !strings.EqualFold("override-conf2-sourcex2", result) {
t.Errorf("Expected '[X] x.two' to be 'override-conf2-sourcex2' but instead it was '%s'", result)
}
_, found = ctx.String("x.notexists")
if found {
t.Error("Config 'x.notexists' shouldn't be found")
}
ctx.SetSection("Y")
result, found = ctx.String("y.three")
if !strings.EqualFold("override-conf2-sourcey3", result) {
t.Errorf("Expected '[Y] y.three' to be 'override-conf2-sourcey3' but instead it was '%s'", result)
}
_, found = ctx.String("y.notexists")
if found {
t.Error("Config 'y.notexists' shouldn't be found")
}
}
func TestLoadContextConfNotFound(t *testing.T) {
_, err := LoadContext("notfound.conf", []string{"testdata/conf-path1"})
if err != nil && !strings.EqualFold("open testdata/conf-path1/notfound.conf: no such file or directory", err.Error()) {
t.Errorf("This is not expected error: %v", err)
}
}
func TestLoadContextInvalidConf(t *testing.T) {
_, err := LoadContext("app-invalid.conf", []string{"testdata"})
if err != nil && !strings.EqualFold("testdata/app-invalid.conf: could not parse line #7: %(two)s + %(four)s", err.Error()) {
t.Errorf("This is not expected error: %v", err)
}
}