gitee.com/mirrors/gauge@v1.0.6/execution/rerun/rerun.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 rerun
    19  
    20  import (
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"sync"
    28  
    29  	"github.com/getgauge/common"
    30  	"github.com/getgauge/gauge/config"
    31  	"github.com/getgauge/gauge/execution/event"
    32  	"github.com/getgauge/gauge/execution/result"
    33  	"github.com/getgauge/gauge/gauge"
    34  	"github.com/getgauge/gauge/gauge_messages"
    35  	"github.com/getgauge/gauge/logger"
    36  	"github.com/getgauge/gauge/util"
    37  )
    38  
    39  const (
    40  	failedFile         = "failures.json"
    41  	lastRunCmdFileName = "lastRunCmd.json"
    42  )
    43  
    44  var failedMeta *failedMetadata
    45  
    46  func init() {
    47  	failedMeta = newFailedMetaData()
    48  }
    49  
    50  type failedMetadata struct {
    51  	Args           []string
    52  	failedItemsMap map[string]map[string]bool
    53  	FailedItems    []string
    54  }
    55  
    56  func (m *failedMetadata) args() []string {
    57  	return append(m.Args, m.FailedItems...)
    58  }
    59  
    60  func (m *failedMetadata) getFailedItems() []string {
    61  	failedItems := []string{}
    62  	for _, v := range m.failedItemsMap {
    63  		for k := range v {
    64  			failedItems = append(failedItems, k)
    65  		}
    66  	}
    67  	return failedItems
    68  }
    69  
    70  func (m *failedMetadata) aggregateFailedItems() {
    71  	m.FailedItems = m.getFailedItems()
    72  }
    73  
    74  func newFailedMetaData() *failedMetadata {
    75  	return &failedMetadata{Args: make([]string, 0), failedItemsMap: make(map[string]map[string]bool), FailedItems: []string{}}
    76  }
    77  
    78  func (m *failedMetadata) addFailedItem(itemName string, item string) {
    79  	if _, ok := m.failedItemsMap[itemName]; !ok {
    80  		m.failedItemsMap[itemName] = make(map[string]bool, 0)
    81  	}
    82  	m.failedItemsMap[itemName][item] = true
    83  }
    84  
    85  // ListenFailedScenarios listens to execution events and writes the failed scenarios to JSON file
    86  func ListenFailedScenarios(wg *sync.WaitGroup, specDirs []string) {
    87  	ch := make(chan event.ExecutionEvent, 0)
    88  	event.Register(ch, event.ScenarioEnd)
    89  	event.Register(ch, event.SpecEnd)
    90  	event.Register(ch, event.SuiteEnd)
    91  	wg.Add(1)
    92  
    93  	go func() {
    94  		for {
    95  			e := <-ch
    96  			switch e.Topic {
    97  			case event.ScenarioEnd:
    98  				prepareScenarioFailedMetadata(e.Result.(*result.ScenarioResult), e.Item.(*gauge.Scenario), e.ExecutionInfo)
    99  			case event.SpecEnd:
   100  				addFailedMetadata(e.Result, specDirs, addSpecFailedMetadata)
   101  			case event.SuiteEnd:
   102  				addFailedMetadata(e.Result, specDirs, addSuiteFailedMetadata)
   103  				failedMeta.aggregateFailedItems()
   104  				writeFailedMeta(getJSON(failedMeta))
   105  				wg.Done()
   106  			}
   107  		}
   108  	}()
   109  }
   110  
   111  func prepareScenarioFailedMetadata(res *result.ScenarioResult, sce *gauge.Scenario, executionInfo gauge_messages.ExecutionInfo) {
   112  	if res.GetFailed() {
   113  		specPath := executionInfo.GetCurrentSpec().GetFileName()
   114  		failedScenario := util.RelPathToProjectRoot(specPath)
   115  		failedMeta.addFailedItem(specPath, fmt.Sprintf("%s:%v", failedScenario, sce.Span.Start))
   116  	}
   117  }
   118  
   119  func addSpecFailedMetadata(res result.Result, args []string) {
   120  	fileName := util.RelPathToProjectRoot(res.(*result.SpecResult).ProtoSpec.GetFileName())
   121  	if _, ok := failedMeta.failedItemsMap[fileName]; ok {
   122  		delete(failedMeta.failedItemsMap, fileName)
   123  	}
   124  	failedMeta.addFailedItem(fileName, fileName)
   125  }
   126  
   127  func addSuiteFailedMetadata(res result.Result, args []string) {
   128  	failedMeta.failedItemsMap = make(map[string]map[string]bool)
   129  	for _, arg := range args {
   130  		path, err := filepath.Abs(arg)
   131  		path = util.RelPathToProjectRoot(path)
   132  		if err == nil {
   133  			failedMeta.addFailedItem(path, path)
   134  		}
   135  	}
   136  }
   137  
   138  func addFailedMetadata(res result.Result, args []string, add func(res result.Result, args []string)) {
   139  	if len(res.GetPostHook()) > 0 || len(res.GetPreHook()) > 0 {
   140  		add(res, args)
   141  	}
   142  }
   143  
   144  func writeFailedMeta(contents string) {
   145  	failuresFile := filepath.Join(config.ProjectRoot, common.DotGauge, failedFile)
   146  	dotGaugeDir := filepath.Join(config.ProjectRoot, common.DotGauge)
   147  	if err := os.MkdirAll(dotGaugeDir, common.NewDirectoryPermissions); err != nil {
   148  		logger.Fatalf(true, "Failed to create directory in %s. Reason: %s", dotGaugeDir, err.Error())
   149  	}
   150  	err := ioutil.WriteFile(failuresFile, []byte(contents), common.NewFilePermissions)
   151  	if err != nil {
   152  		logger.Fatalf(true, "Failed to write to %s. Reason: %s", failuresFile, err.Error())
   153  	}
   154  }
   155  
   156  func getJSON(failedMeta *failedMetadata) string {
   157  	j, err := json.MarshalIndent(failedMeta, "", "\t")
   158  	if err != nil {
   159  		logger.Warningf(true, "Failed to save run info. Reason: %s", err.Error())
   160  	}
   161  	return string(j)
   162  }
   163  
   164  var GetLastFailedState = func() ([]string, error) {
   165  	meta := readLastFailedState()
   166  	util.SetWorkingDir(config.ProjectRoot)
   167  	if len(meta.FailedItems) == 0 {
   168  		return nil, errors.New("No failed tests found.")
   169  	}
   170  	return meta.args(), nil
   171  }
   172  
   173  func SaveState(args []string, specs []string) {
   174  	isPresent := func(values []string, value string) bool {
   175  		for _, v := range values {
   176  			if v == value {
   177  				return true
   178  			}
   179  		}
   180  		return false
   181  	}
   182  	for _, a := range args {
   183  		if !isPresent(specs, a) {
   184  			failedMeta.Args = append(failedMeta.Args, a)
   185  		}
   186  	}
   187  }
   188  
   189  func readLastFailedState() *failedMetadata {
   190  	contents, err := common.ReadFileContents(filepath.Join(config.ProjectRoot, common.DotGauge, failedFile))
   191  	if err != nil {
   192  		logger.Fatalf(true, "Failed to read last run information. Reason: %s", err.Error())
   193  	}
   194  	meta := newFailedMetaData()
   195  	if err = json.Unmarshal([]byte(contents), meta); err != nil {
   196  		logger.Fatalf(true, "Invalid last run information. Reason: %s", err.Error())
   197  	}
   198  	return meta
   199  }
   200  
   201  var ReadPrevArgs = func() []string {
   202  	contents, err := common.ReadFileContents(filepath.Join(config.ProjectRoot, common.DotGauge, lastRunCmdFileName))
   203  	if err != nil {
   204  		logger.Fatalf(true, "Failed to read previous command information. Reason: %s", err.Error())
   205  		return nil
   206  	}
   207  	var args []string
   208  	if err = json.Unmarshal([]byte(contents), &args); err != nil {
   209  		logger.Fatalf(true, "Invalid previous command information. Reason: %s", err.Error())
   210  		return nil
   211  	}
   212  	return args
   213  }
   214  
   215  var WritePrevArgs = func(cmdArgs []string) {
   216  	b, err := json.MarshalIndent(cmdArgs, "", "\t")
   217  	if err != nil {
   218  		logger.Fatalf(true, "Unable to parse last run command. Error : %v", err.Error())
   219  	}
   220  	prevCmdFile := filepath.Join(config.ProjectRoot, common.DotGauge, lastRunCmdFileName)
   221  	dotGaugeDir := filepath.Join(config.ProjectRoot, common.DotGauge)
   222  	if err = os.MkdirAll(dotGaugeDir, common.NewDirectoryPermissions); err != nil {
   223  		logger.Fatalf(true, "Failed to create directory in %s. Reason: %s", dotGaugeDir, err.Error())
   224  	}
   225  	err = ioutil.WriteFile(prevCmdFile, b, common.NewFilePermissions)
   226  	if err != nil {
   227  		logger.Fatalf(true, "Failed to write to %s. Reason: %s", prevCmdFile, err.Error())
   228  	}
   229  }