github.com/mattdotmatt/gauge@v0.3.2-0.20160421115137-425a4cdccb62/parser/resolver.go (about) 1 // Copyright 2015 ThoughtWorks, Inc. 2 3 // This file is part of Gauge. 4 5 // Gauge is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 10 // Gauge is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 15 // You should have received a copy of the GNU General Public License 16 // along with Gauge. If not, see <http://www.gnu.org/licenses/>. 17 18 package parser 19 20 import ( 21 "fmt" 22 "regexp" 23 "strings" 24 25 "github.com/getgauge/common" 26 "github.com/getgauge/gauge/gauge" 27 "github.com/getgauge/gauge/gauge_messages" 28 "github.com/getgauge/gauge/util" 29 "github.com/golang/protobuf/proto" 30 ) 31 32 type invalidSpecialParamError struct { 33 message string 34 } 35 36 type resolverFn func(string) (*gauge.StepArg, error) 37 type specialTypeResolver struct { 38 predefinedResolvers map[string]resolverFn 39 } 40 41 type ParamResolver struct { 42 } 43 44 func (invalidSpecialParamError invalidSpecialParamError) Error() string { 45 return invalidSpecialParamError.message 46 } 47 48 func (paramResolver *ParamResolver) GetResolvedParams(step *gauge.Step, parent *gauge.Step, dataTableLookup *gauge.ArgLookup) []*gauge_messages.Parameter { 49 parameters := make([]*gauge_messages.Parameter, 0) 50 for _, arg := range step.Args { 51 parameter := new(gauge_messages.Parameter) 52 parameter.Name = proto.String(arg.Name) 53 if arg.ArgType == gauge.Static { 54 parameter.ParameterType = gauge_messages.Parameter_Static.Enum() 55 parameter.Value = proto.String(arg.Value) 56 } else if arg.ArgType == gauge.Dynamic { 57 var resolvedArg *gauge.StepArg 58 if parent != nil { 59 resolvedArg = parent.GetArg(arg.Value) 60 } else { 61 resolvedArg = dataTableLookup.GetArg(arg.Value) 62 } 63 //In case a special table used in a concept, you will get a dynamic table value which has to be resolved from the concept lookup 64 parameter.Name = proto.String(resolvedArg.Name) 65 if resolvedArg.Table.IsInitialized() { 66 parameter.ParameterType = gauge_messages.Parameter_Special_Table.Enum() 67 parameter.Table = paramResolver.createProtoStepTable(&resolvedArg.Table, dataTableLookup) 68 } else { 69 parameter.ParameterType = gauge_messages.Parameter_Dynamic.Enum() 70 parameter.Value = proto.String(resolvedArg.Value) 71 } 72 } else if arg.ArgType == gauge.SpecialString { 73 parameter.ParameterType = gauge_messages.Parameter_Special_String.Enum() 74 parameter.Value = proto.String(arg.Value) 75 } else if arg.ArgType == gauge.SpecialTable { 76 parameter.ParameterType = gauge_messages.Parameter_Special_Table.Enum() 77 parameter.Table = paramResolver.createProtoStepTable(&arg.Table, dataTableLookup) 78 } else { 79 parameter.ParameterType = gauge_messages.Parameter_Table.Enum() 80 parameter.Table = paramResolver.createProtoStepTable(&arg.Table, dataTableLookup) 81 82 } 83 parameters = append(parameters, parameter) 84 } 85 86 return parameters 87 88 } 89 90 func (resolver *ParamResolver) createProtoStepTable(table *gauge.Table, dataTableLookup *gauge.ArgLookup) *gauge_messages.ProtoTable { 91 protoTable := new(gauge_messages.ProtoTable) 92 protoTable.Headers = &gauge_messages.ProtoTableRow{Cells: table.Headers} 93 tableRows := make([]*gauge_messages.ProtoTableRow, 0) 94 if len(table.Columns) == 0 { 95 protoTable.Rows = tableRows 96 return protoTable 97 } 98 for i := 0; i < len(table.Columns[0]); i++ { 99 row := make([]string, 0) 100 for _, header := range table.Headers { 101 tableCell := table.Get(header)[i] 102 value := tableCell.Value 103 if tableCell.CellType == gauge.Dynamic { 104 //if concept has a table with dynamic cell, fetch from datatable 105 value = dataTableLookup.GetArg(tableCell.Value).Value 106 } 107 row = append(row, value) 108 } 109 tableRows = append(tableRows, &gauge_messages.ProtoTableRow{Cells: row}) 110 } 111 protoTable.Rows = tableRows 112 return protoTable 113 } 114 115 func newSpecialTypeResolver() *specialTypeResolver { 116 resolver := new(specialTypeResolver) 117 resolver.predefinedResolvers = initializePredefinedResolvers() 118 return resolver 119 } 120 121 func initializePredefinedResolvers() map[string]resolverFn { 122 return map[string]resolverFn{ 123 "file": func(filePath string) (*gauge.StepArg, error) { 124 fileContent, err := common.ReadFileContents(util.GetPathToFile(filePath)) 125 if err != nil { 126 return nil, err 127 } 128 return &gauge.StepArg{Value: fileContent, ArgType: gauge.SpecialString}, nil 129 }, 130 "table": func(filePath string) (*gauge.StepArg, error) { 131 csv, err := common.ReadFileContents(util.GetPathToFile(filePath)) 132 if err != nil { 133 return nil, err 134 } 135 csvTable, err := convertCsvToTable(csv) 136 if err != nil { 137 return nil, err 138 } 139 return &gauge.StepArg{Table: *csvTable, ArgType: gauge.SpecialTable}, nil 140 }, 141 } 142 } 143 144 func (resolver *specialTypeResolver) resolve(arg string) (*gauge.StepArg, error) { 145 if util.IsWindows() { 146 arg = GetUnescapedString(arg) 147 } 148 regEx := regexp.MustCompile("(.*?):(.*)") 149 match := regEx.FindAllStringSubmatch(arg, -1) 150 specialType := strings.TrimSpace(match[0][1]) 151 value := strings.TrimSpace(match[0][2]) 152 stepArg, err := resolver.getStepArg(specialType, value, arg) 153 if err == nil { 154 stepArg.Name = arg 155 } 156 return stepArg, err 157 } 158 159 func (resolver *specialTypeResolver) getStepArg(specialType string, value string, arg string) (*gauge.StepArg, error) { 160 resolveFunc, found := resolver.predefinedResolvers[specialType] 161 if found { 162 return resolveFunc(value) 163 } 164 return nil, invalidSpecialParamError{message: fmt.Sprintf("Resolver not found for special param <%s>", arg)} 165 } 166 167 // Creating a copy of the lookup and populating table values 168 func PopulateConceptDynamicParams(concept *gauge.Step, dataTableLookup *gauge.ArgLookup) { 169 //If it is a top level concept 170 if concept.Parent == nil { 171 lookup := concept.Lookup.GetCopy() 172 for key, _ := range lookup.ParamIndexMap { 173 conceptLookupArg := lookup.GetArg(key) 174 if conceptLookupArg.ArgType == gauge.Dynamic { 175 resolvedArg := dataTableLookup.GetArg(conceptLookupArg.Value) 176 lookup.AddArgValue(key, resolvedArg) 177 } 178 } 179 concept.Lookup = *lookup 180 } 181 182 //Updating values inside the concept step as well 183 newArgs := make([]*gauge.StepArg, 0) 184 for _, arg := range concept.Args { 185 if arg.ArgType == gauge.Dynamic { 186 if concept.Parent != nil { 187 newArgs = append(newArgs, concept.Parent.GetArg(arg.Value)) 188 } else { 189 newArgs = append(newArgs, dataTableLookup.GetArg(arg.Value)) 190 } 191 } else { 192 newArgs = append(newArgs, arg) 193 } 194 } 195 concept.Args = newArgs 196 concept.PopulateFragments() 197 }