github.com/ezbuy/gauge@v0.9.4-0.20171013092048-7ac5bd3931cd/conceptExtractor/conceptExtractor.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 conceptExtractor
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"regexp"
    25  	"strings"
    26  
    27  	"path"
    28  
    29  	"github.com/getgauge/common"
    30  	"github.com/getgauge/gauge/formatter"
    31  	"github.com/getgauge/gauge/gauge"
    32  	"github.com/getgauge/gauge/gauge_messages"
    33  	"github.com/getgauge/gauge/parser"
    34  	"github.com/getgauge/gauge/util"
    35  )
    36  
    37  const (
    38  	SPEC_HEADING_TEMPLATE = "# S\n\n"
    39  	TABLE                 = "table"
    40  )
    41  
    42  type extractor struct {
    43  	conceptName    string
    44  	conceptStep    *gauge.Step
    45  	stepsToExtract []*gauge_messages.Step
    46  	stepsInConcept string
    47  	table          *gauge.Table
    48  	fileContent    string
    49  	dynamicArgs    []string
    50  	errors         []error
    51  }
    52  
    53  func ExtractConcept(conceptName *gauge_messages.Step, steps []*gauge_messages.Step, conceptFileName string, changeAcrossProject bool, selectedTextInfo *gauge_messages.TextInfo) (bool, error, []string) {
    54  	content := SPEC_HEADING_TEMPLATE
    55  	if util.IsSpec(selectedTextInfo.GetFileName()) {
    56  		content, _ = common.ReadFileContents(selectedTextInfo.GetFileName())
    57  	}
    58  	concept, conceptUsageText, err := getExtractedConcept(conceptName, steps, content, selectedTextInfo.GetFileName())
    59  	if err != nil {
    60  		return false, err, []string{}
    61  	}
    62  	writeConceptToFile(concept, conceptUsageText, conceptFileName, selectedTextInfo.GetFileName(), selectedTextInfo)
    63  	return true, errors.New(""), []string{conceptFileName, selectedTextInfo.GetFileName()}
    64  }
    65  
    66  func ReplaceExtractedStepsWithConcept(selectedTextInfo *gauge_messages.TextInfo, conceptText string) string {
    67  	content, _ := common.ReadFileContents(selectedTextInfo.GetFileName())
    68  	return replaceText(content, selectedTextInfo, conceptText)
    69  }
    70  
    71  func replaceText(content string, info *gauge_messages.TextInfo, replacement string) string {
    72  	parts := regexp.MustCompile("\r\n|\n").Split(content, -1)
    73  	for i := info.GetStartingLineNo(); i < info.GetEndLineNo(); i++ {
    74  		parts = append(parts[:info.GetStartingLineNo()], parts[info.GetStartingLineNo()+1:]...)
    75  	}
    76  	parts[info.GetStartingLineNo()-1] = replacement
    77  	return strings.Join(parts, "\n")
    78  }
    79  
    80  func writeConceptToFile(concept string, conceptUsageText string, conceptFileName string, fileName string, info *gauge_messages.TextInfo) {
    81  	if _, err := os.Stat(conceptFileName); os.IsNotExist(err) {
    82  		basepath := path.Dir(conceptFileName)
    83  		if _, err := os.Stat(basepath); os.IsNotExist(err) {
    84  			os.MkdirAll(basepath, common.NewDirectoryPermissions)
    85  		}
    86  		os.Create(conceptFileName)
    87  	}
    88  	content, _ := common.ReadFileContents(conceptFileName)
    89  	util.SaveFile(conceptFileName, content+"\n"+concept, true)
    90  	text := ReplaceExtractedStepsWithConcept(info, conceptUsageText)
    91  	util.SaveFile(fileName, text, true)
    92  }
    93  
    94  func getExtractedConcept(conceptName *gauge_messages.Step, steps []*gauge_messages.Step, content string, cptFileName string) (string, string, error) {
    95  	tokens, _ := new(parser.SpecParser).GenerateTokens("* "+conceptName.GetName(), cptFileName)
    96  	conceptStep, _ := parser.CreateStepUsingLookup(tokens[0], nil, cptFileName)
    97  	cptDict, _ := parser.ParseConcepts()
    98  	if isDuplicateConcept(conceptStep, cptDict) {
    99  		return "", "", fmt.Errorf("Concept `%s` already present", conceptName.GetName())
   100  	}
   101  	specText, err := getContentWithDataTable(content, cptFileName)
   102  	if err != nil {
   103  		return "", "", err
   104  	}
   105  	extractor := &extractor{conceptName: "* " + conceptName.GetName(), stepsInConcept: "", stepsToExtract: steps, conceptStep: conceptStep, table: &gauge.Table{}, fileContent: specText, errors: make([]error, 0)}
   106  	extractor.extractSteps(cptFileName)
   107  	if len(extractor.errors) != 0 {
   108  		return "", "", err
   109  	}
   110  	conceptStep.ReplaceArgsWithDynamic(conceptStep.Args)
   111  	addArgsFromTable(conceptStep, &extractor.conceptName, extractor.dynamicArgs)
   112  	if extractor.table.IsInitialized() {
   113  		extractor.conceptName += "\n" + formatter.FormatTable(extractor.table)
   114  	}
   115  	return strings.Replace(formatter.FormatStep(conceptStep), "* ", "# ", 1) + (extractor.stepsInConcept), extractor.conceptName, nil
   116  }
   117  
   118  func addArgsFromTable(concept *gauge.Step, conceptName *string, args []string) {
   119  	for _, arg := range args {
   120  		concept.Value += " {}"
   121  		concept.Args = append(concept.Args, &gauge.StepArg{Value: arg, ArgType: gauge.Dynamic, Name: arg})
   122  		*conceptName += fmt.Sprintf(" <%s>", arg)
   123  	}
   124  }
   125  
   126  func getContentWithDataTable(content, cptFileName string) (string, error) {
   127  	spec, result := new(parser.SpecParser).Parse(content, &gauge.ConceptDictionary{}, cptFileName)
   128  	if !result.Ok {
   129  		return "", fmt.Errorf("Spec Parse failure: %s", result.ParseErrors)
   130  	}
   131  	newSpec := &gauge.Specification{Heading: &gauge.Heading{Value: "SPECHEADING"}}
   132  	if spec.DataTable.IsInitialized() {
   133  		newSpec = &gauge.Specification{Items: []gauge.Item{&spec.DataTable}, Heading: &gauge.Heading{Value: "SPECHEADING"}}
   134  	}
   135  	return formatter.FormatSpecification(newSpec) + "\n##hello \n* step \n", nil
   136  }
   137  
   138  func isDuplicateConcept(concept *gauge.Step, cptDict *gauge.ConceptDictionary) bool {
   139  	for _, cpt := range cptDict.ConceptsMap {
   140  		if strings.TrimSpace(cpt.ConceptStep.Value) == strings.TrimSpace(concept.Value) {
   141  			return true
   142  		}
   143  	}
   144  	return false
   145  }
   146  
   147  func (e *extractor) extractSteps(cptFileName string) {
   148  	for _, step := range e.stepsToExtract {
   149  		tokens, _ := new(parser.SpecParser).GenerateTokens("*"+step.GetName(), cptFileName)
   150  		stepInConcept, _ := parser.CreateStepUsingLookup(tokens[0], nil, cptFileName)
   151  		if step.GetTable() != "" {
   152  			e.handleTable(stepInConcept, step, cptFileName)
   153  		}
   154  		stepInConcept.ReplaceArgsWithDynamic(e.conceptStep.Args)
   155  		e.stepsInConcept += formatter.FormatStep(stepInConcept)
   156  	}
   157  }
   158  
   159  func (e *extractor) handleTable(stepInConcept *gauge.Step, step *gauge_messages.Step, cptFileName string) {
   160  	stepInConcept.Value += " {}"
   161  	specText := e.fileContent + step.GetTable()
   162  	spec, result := new(parser.SpecParser).Parse(specText, &gauge.ConceptDictionary{}, cptFileName)
   163  	if !result.Ok {
   164  		for _, err := range result.ParseErrors {
   165  			e.errors = append(e.errors, err)
   166  		}
   167  		return
   168  	}
   169  	stepArgs := []*gauge.StepArg{spec.Scenarios[0].Steps[0].Args[0]}
   170  	e.addTableAsParam(step, stepArgs)
   171  	stepInConcept.Args = append(stepInConcept.Args, stepArgs[0])
   172  }
   173  
   174  func (e *extractor) addTableAsParam(step *gauge_messages.Step, args []*gauge.StepArg) {
   175  	if step.GetParamTableName() != "" {
   176  		e.conceptName = strings.Replace(e.conceptName, fmt.Sprintf("<%s>", step.GetParamTableName()), "", 1)
   177  		e.table = &args[0].Table
   178  		args[0] = &gauge.StepArg{Value: step.GetParamTableName(), ArgType: gauge.Dynamic}
   179  	} else {
   180  		e.dynamicArgs = append(e.dynamicArgs, (&args[0].Table).GetDynamicArgs()...)
   181  	}
   182  }