|  | package spec | 
|  |  | 
|  | import ( | 
|  | "math/rand" | 
|  | "regexp" | 
|  | "sort" | 
|  | ) | 
|  |  | 
|  | type Specs struct { | 
|  | specs                []*Spec | 
|  | hasProgrammaticFocus bool | 
|  | RegexScansFilePath   bool | 
|  | } | 
|  |  | 
|  | func NewSpecs(specs []*Spec) *Specs { | 
|  | return &Specs{ | 
|  | specs: specs, | 
|  | } | 
|  | } | 
|  |  | 
|  | func (e *Specs) Specs() []*Spec { | 
|  | return e.specs | 
|  | } | 
|  |  | 
|  | func (e *Specs) HasProgrammaticFocus() bool { | 
|  | return e.hasProgrammaticFocus | 
|  | } | 
|  |  | 
|  | func (e *Specs) Shuffle(r *rand.Rand) { | 
|  | sort.Sort(e) | 
|  | permutation := r.Perm(len(e.specs)) | 
|  | shuffledSpecs := make([]*Spec, len(e.specs)) | 
|  | for i, j := range permutation { | 
|  | shuffledSpecs[i] = e.specs[j] | 
|  | } | 
|  | e.specs = shuffledSpecs | 
|  | } | 
|  |  | 
|  | func (e *Specs) ApplyFocus(description string, focusString string, skipString string) { | 
|  | if focusString == "" && skipString == "" { | 
|  | e.applyProgrammaticFocus() | 
|  | } else { | 
|  | e.applyRegExpFocusAndSkip(description, focusString, skipString) | 
|  | } | 
|  | } | 
|  |  | 
|  | func (e *Specs) applyProgrammaticFocus() { | 
|  | e.hasProgrammaticFocus = false | 
|  | for _, spec := range e.specs { | 
|  | if spec.Focused() && !spec.Pending() { | 
|  | e.hasProgrammaticFocus = true | 
|  | break | 
|  | } | 
|  | } | 
|  |  | 
|  | if e.hasProgrammaticFocus { | 
|  | for _, spec := range e.specs { | 
|  | if !spec.Focused() { | 
|  | spec.Skip() | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // toMatch returns a byte[] to be used by regex matchers.  When adding new behaviours to the matching function, | 
|  | // this is the place which we append to. | 
|  | func (e *Specs) toMatch(description string, spec *Spec) []byte { | 
|  | if e.RegexScansFilePath { | 
|  | return []byte( | 
|  | description + " " + | 
|  | spec.ConcatenatedString() + " " + | 
|  | spec.subject.CodeLocation().FileName) | 
|  | } else { | 
|  | return []byte( | 
|  | description + " " + | 
|  | spec.ConcatenatedString()) | 
|  | } | 
|  | } | 
|  |  | 
|  | func (e *Specs) applyRegExpFocusAndSkip(description string, focusString string, skipString string) { | 
|  | for _, spec := range e.specs { | 
|  | matchesFocus := true | 
|  | matchesSkip := false | 
|  |  | 
|  | toMatch := e.toMatch(description, spec) | 
|  |  | 
|  | if focusString != "" { | 
|  | focusFilter := regexp.MustCompile(focusString) | 
|  | matchesFocus = focusFilter.Match([]byte(toMatch)) | 
|  | } | 
|  |  | 
|  | if skipString != "" { | 
|  | skipFilter := regexp.MustCompile(skipString) | 
|  | matchesSkip = skipFilter.Match([]byte(toMatch)) | 
|  | } | 
|  |  | 
|  | if !matchesFocus || matchesSkip { | 
|  | spec.Skip() | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func (e *Specs) SkipMeasurements() { | 
|  | for _, spec := range e.specs { | 
|  | if spec.IsMeasurement() { | 
|  | spec.Skip() | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //sort.Interface | 
|  |  | 
|  | func (e *Specs) Len() int { | 
|  | return len(e.specs) | 
|  | } | 
|  |  | 
|  | func (e *Specs) Less(i, j int) bool { | 
|  | return e.specs[i].ConcatenatedString() < e.specs[j].ConcatenatedString() | 
|  | } | 
|  |  | 
|  | func (e *Specs) Swap(i, j int) { | 
|  | e.specs[i], e.specs[j] = e.specs[j], e.specs[i] | 
|  | } |