github.com/ezbuy/gauge@v0.9.4-0.20171013092048-7ac5bd3931cd/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  )
    30  
    31  type invalidSpecialParamError struct {
    32  	message string
    33  }
    34  
    35  type resolverFn func(string) (*gauge.StepArg, error)
    36  type specialTypeResolver struct {
    37  	predefinedResolvers map[string]resolverFn
    38  }
    39  
    40  type ParamResolver struct {
    41  }
    42  
    43  func (invalidSpecialParamError invalidSpecialParamError) Error() string {
    44  	return invalidSpecialParamError.message
    45  }
    46  
    47  func (paramResolver *ParamResolver) GetResolvedParams(step *gauge.Step, parent *gauge.Step, lookup *gauge.ArgLookup) []*gauge_messages.Parameter {
    48  	parameters := make([]*gauge_messages.Parameter, 0)
    49  	for _, arg := range step.Args {
    50  		parameter := new(gauge_messages.Parameter)
    51  		parameter.Name = arg.Name
    52  		if arg.ArgType == gauge.Static {
    53  			parameter.ParameterType = gauge_messages.Parameter_Static
    54  			parameter.Value = arg.Value
    55  		} else if arg.ArgType == gauge.Dynamic {
    56  			var resolvedArg *gauge.StepArg
    57  			if parent != nil {
    58  				resolvedArg = parent.GetArg(arg.Value)
    59  			} else {
    60  				resolvedArg = lookup.GetArg(arg.Value)
    61  			}
    62  			//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
    63  			parameter.Name = resolvedArg.Name
    64  			if resolvedArg.Table.IsInitialized() {
    65  				parameter.ParameterType = gauge_messages.Parameter_Special_Table
    66  				parameter.Table = paramResolver.createProtoStepTable(&resolvedArg.Table, lookup)
    67  			} else {
    68  				parameter.ParameterType = gauge_messages.Parameter_Dynamic
    69  				parameter.Value = resolvedArg.Value
    70  			}
    71  		} else if arg.ArgType == gauge.SpecialString {
    72  			parameter.ParameterType = gauge_messages.Parameter_Special_String
    73  			parameter.Value = arg.Value
    74  		} else if arg.ArgType == gauge.SpecialTable {
    75  			parameter.ParameterType = gauge_messages.Parameter_Special_Table
    76  			parameter.Table = paramResolver.createProtoStepTable(&arg.Table, lookup)
    77  		} else {
    78  			parameter.ParameterType = gauge_messages.Parameter_Table
    79  			parameter.Table = paramResolver.createProtoStepTable(&arg.Table, lookup)
    80  
    81  		}
    82  		parameters = append(parameters, parameter)
    83  	}
    84  
    85  	return parameters
    86  
    87  }
    88  
    89  func (resolver *ParamResolver) createProtoStepTable(table *gauge.Table, lookup *gauge.ArgLookup) *gauge_messages.ProtoTable {
    90  	protoTable := new(gauge_messages.ProtoTable)
    91  	protoTable.Headers = &gauge_messages.ProtoTableRow{Cells: table.Headers}
    92  	tableRows := make([]*gauge_messages.ProtoTableRow, 0)
    93  	if len(table.Columns) == 0 {
    94  		protoTable.Rows = tableRows
    95  		return protoTable
    96  	}
    97  	for i := 0; i < len(table.Columns[0]); i++ {
    98  		row := make([]string, 0)
    99  		for _, header := range table.Headers {
   100  			tableCell := table.Get(header)[i]
   101  			value := tableCell.Value
   102  			if tableCell.CellType == gauge.Dynamic {
   103  				//if concept has a table with dynamic cell, fetch from datatable
   104  				value = lookup.GetArg(tableCell.Value).Value
   105  			}
   106  			row = append(row, value)
   107  		}
   108  		tableRows = append(tableRows, &gauge_messages.ProtoTableRow{Cells: row})
   109  	}
   110  	protoTable.Rows = tableRows
   111  	return protoTable
   112  }
   113  
   114  func newSpecialTypeResolver() *specialTypeResolver {
   115  	resolver := new(specialTypeResolver)
   116  	resolver.predefinedResolvers = initializePredefinedResolvers()
   117  	return resolver
   118  }
   119  
   120  func initializePredefinedResolvers() map[string]resolverFn {
   121  	return map[string]resolverFn{
   122  		"file": func(filePath string) (*gauge.StepArg, error) {
   123  			fileContent, err := common.ReadFileContents(util.GetPathToFile(filePath))
   124  			if err != nil {
   125  				return nil, err
   126  			}
   127  			return &gauge.StepArg{Value: fileContent, ArgType: gauge.SpecialString}, nil
   128  		},
   129  		"table": func(filePath string) (*gauge.StepArg, error) {
   130  			csv, err := common.ReadFileContents(util.GetPathToFile(filePath))
   131  			if err != nil {
   132  				return nil, err
   133  			}
   134  			csvTable, err := convertCsvToTable(csv)
   135  			if err != nil {
   136  				return nil, err
   137  			}
   138  			return &gauge.StepArg{Table: *csvTable, ArgType: gauge.SpecialTable}, nil
   139  		},
   140  	}
   141  }
   142  
   143  func (resolver *specialTypeResolver) resolve(arg string) (*gauge.StepArg, error) {
   144  	if util.IsWindows() {
   145  		arg = GetUnescapedString(arg)
   146  	}
   147  	regEx := regexp.MustCompile("(.*?):(.*)")
   148  	match := regEx.FindAllStringSubmatch(arg, -1)
   149  	specialType := strings.TrimSpace(match[0][1])
   150  	value := strings.TrimSpace(match[0][2])
   151  	stepArg, err := resolver.getStepArg(specialType, value, arg)
   152  	if err == nil {
   153  		stepArg.Name = arg
   154  	}
   155  	return stepArg, err
   156  }
   157  
   158  func (resolver *specialTypeResolver) getStepArg(specialType string, value string, arg string) (*gauge.StepArg, error) {
   159  	resolveFunc, found := resolver.predefinedResolvers[specialType]
   160  	if found {
   161  		return resolveFunc(value)
   162  	}
   163  	return nil, invalidSpecialParamError{message: fmt.Sprintf("Resolver not found for special param <%s>", arg)}
   164  }
   165  
   166  // Creating a copy of the lookup and populating table values
   167  func PopulateConceptDynamicParams(concept *gauge.Step, dataTableLookup *gauge.ArgLookup) {
   168  	//If it is a top level concept
   169  	lookup := concept.Lookup.GetCopy()
   170  	for key := range lookup.ParamIndexMap {
   171  		conceptLookupArg := lookup.GetArg(key)
   172  		if conceptLookupArg.ArgType == gauge.Dynamic {
   173  			resolvedArg := dataTableLookup.GetArg(conceptLookupArg.Value)
   174  			lookup.AddArgValue(key, resolvedArg)
   175  		}
   176  	}
   177  	concept.Lookup = *lookup
   178  
   179  	//Updating values inside the concept step as well
   180  	newArgs := make([]*gauge.StepArg, 0)
   181  	for _, arg := range concept.Args {
   182  		if arg.ArgType == gauge.Dynamic {
   183  			if concept.Parent != nil {
   184  				newArgs = append(newArgs, concept.Parent.GetArg(arg.Value))
   185  			} else {
   186  				newArgs = append(newArgs, dataTableLookup.GetArg(arg.Value))
   187  			}
   188  		} else {
   189  			newArgs = append(newArgs, arg)
   190  		}
   191  	}
   192  	concept.Args = newArgs
   193  	concept.PopulateFragments()
   194  }