blob: d0d80498cc19601f6c36dbe9ea48ee39ad43e5d6 [file] [log] [blame]
/*
* Copyright 2020-present Open Networking Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package flow
import (
"bufio"
"context"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"testing"
)
// TestLoadersIdentical ensures that the group, flow, and meter loaders always have an identical implementation.
func TestLoadersIdentical(t *testing.T) {
types := []string{"flow", "group"}
identical := [][]string{
{`ofp\.OfpFlowStats`, `ofp\.OfpGroupEntry`},
{`\.Id`, `\.Desc\.GroupId`},
{`uint64`, `uint32`},
{`Flow`, `Group`},
{`flow`, `group`},
}
regexes := make([][]*regexp.Regexp, len(identical[0]))
for i := range regexes {
regexes[i] = make([]*regexp.Regexp, len(identical))
}
for i, group := range identical {
for j, regexStr := range group {
// convert from column-wise to row-wise for convenience
regexes[j][i] = regexp.MustCompile(regexStr)
}
}
for i := 1; i < len(types); i++ {
if err := compare(regexes[0], regexes[i],
"../"+types[0]+"/cache.go",
"../"+types[i]+"/cache.go"); err != nil {
t.Error(err)
return
}
}
}
func compare(regexesA, regexesB []*regexp.Regexp, fileNameA, fileNameB string) error {
fileA, err := os.Open(filepath.Clean(fileNameA))
if err != nil {
return err
}
defer func() {
err := fileA.Close()
if err != nil {
logger.Errorf(context.Background(), "failed to close file: %v", err)
}
}()
fileB, err := os.Open(filepath.Clean(fileNameB))
if err != nil {
return err
}
defer func() {
err := fileB.Close()
if err != nil {
logger.Errorf(context.Background(), "failed to close file: %v", err)
}
}()
scannerA, scannerB := bufio.NewScanner(fileA), bufio.NewScanner(fileB)
// treat any number of spaces as a single space
spaceRegex := regexp.MustCompile(` +`)
// extra lines are permitted before a "blank" line, or before a lock/unlock
spacerRegex := regexp.MustCompile(`^(?:[^a-z]*|.*Lock\(\)|.*Unlock\(\))$`)
// ignore import type differences
libGoImportRegex := regexp.MustCompile(`^.*github\.com/opencord/voltha-protos/.*$`)
lineA, lineB := 1, 1
linesLoop:
for {
if continueA, continueB := scannerA.Scan(), scannerB.Scan(); !continueA || !continueB {
// EOF
break linesLoop
}
textA, textB := scannerA.Text(), scannerB.Text()
// allow any number of "extra" lines just before a spacer line
for {
isSpacerA, isSpacerB := spacerRegex.MatchString(textA), spacerRegex.MatchString(textB)
if isSpacerA && !isSpacerB {
if !scannerB.Scan() {
// EOF
break linesLoop
}
lineB++
textB = scannerB.Text()
continue
} else if isSpacerB && !isSpacerA {
if !scannerA.Scan() {
// EOF
break linesLoop
}
lineA++
textA = scannerA.Text()
continue
}
break
}
replacedA, replacedB := textA, textB
for i := range regexesA {
replacement := "{{type" + strconv.Itoa(i) + "}}"
replacedA, replacedB = regexesA[i].ReplaceAllString(replacedA, replacement), regexesB[i].ReplaceAllString(replacedB, replacement)
}
// replace multiple spaces with single space
replacedA, replacedB = spaceRegex.ReplaceAllString(replacedA, " "), spaceRegex.ReplaceAllString(replacedB, " ")
// ignore voltha-protos import of ofp vs voltha
replacedA, replacedB = libGoImportRegex.ReplaceAllString(replacedA, "{{lib-go-import}}"), libGoImportRegex.ReplaceAllString(replacedB, "{{lib-go-import}}")
if replacedA != replacedB && textA != textB {
return fmt.Errorf("files which must be identical do not match: \n %s:%d\n %s\n %s:%d\n %s\n\n\t%s\n\t%s", fileNameA, lineA, textA, fileNameB, lineB, textB, replacedA, replacedB)
}
lineA++
lineB++
}
if err := scannerA.Err(); err != nil {
return err
}
if err := scannerB.Err(); err != nil {
return err
}
return nil
}