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  }