github.com/anchore/syft@v1.38.2/internal/regex_helpers_test.go (about)

     1  package internal
     2  
     3  import (
     4  	"regexp"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestMatchCaptureGroups(t *testing.T) {
    13  	tests := []struct {
    14  		name     string
    15  		input    string
    16  		pattern  string
    17  		expected map[string]string
    18  	}{
    19  		{
    20  			name:    "go-case",
    21  			input:   "match this thing",
    22  			pattern: `(?P<name>match).*(?P<version>thing)`,
    23  			expected: map[string]string{
    24  				"name":    "match",
    25  				"version": "thing",
    26  			},
    27  		},
    28  		{
    29  			name:    "only matches the first instance",
    30  			input:   "match this thing batch another think",
    31  			pattern: `(?P<name>[mb]atch).*?(?P<version>thin[gk])`,
    32  			expected: map[string]string{
    33  				"name":    "match",
    34  				"version": "thing",
    35  			},
    36  		},
    37  		{
    38  			name:    "nested capture groups",
    39  			input:   "cool something to match against",
    40  			pattern: `((?P<name>match) (?P<version>against))`,
    41  			expected: map[string]string{
    42  				"name":    "match",
    43  				"version": "against",
    44  			},
    45  		},
    46  		{
    47  			name:    "nested optional capture groups",
    48  			input:   "cool something to match against",
    49  			pattern: `((?P<name>match) (?P<version>against))?`,
    50  			expected: map[string]string{
    51  				"name":    "match",
    52  				"version": "against",
    53  			},
    54  		},
    55  		{
    56  			name:    "nested optional capture groups with larger match",
    57  			input:   "cool something to match against match never",
    58  			pattern: `.*?((?P<name>match) (?P<version>(against|never)))?`,
    59  			expected: map[string]string{
    60  				"name":    "match",
    61  				"version": "against",
    62  			},
    63  		},
    64  	}
    65  
    66  	for _, test := range tests {
    67  		t.Run(test.name, func(t *testing.T) {
    68  			actual := MatchNamedCaptureGroups(regexp.MustCompile(test.pattern), test.input)
    69  			assert.Equal(t, test.expected, actual)
    70  		})
    71  	}
    72  }
    73  
    74  func TestMatchNamedCaptureGroupsFromReader(t *testing.T) {
    75  	tests := []struct {
    76  		name    string
    77  		pattern string
    78  		input   string
    79  		want    map[string]string
    80  		wantErr require.ErrorAssertionFunc
    81  	}{
    82  		{
    83  			name:    "match single group",
    84  			pattern: `(?P<key>[^1-9]+)`,
    85  			input:   "key",
    86  			want:    map[string]string{"key": "key"},
    87  			wantErr: require.NoError,
    88  		},
    89  		{
    90  			name:    "match multiple groups",
    91  			pattern: `(?P<key>[^1-9]+):(?P<value>\w+)`,
    92  			input:   "key:value",
    93  			want:    map[string]string{"key": "key", "value": "value"},
    94  			wantErr: require.NoError,
    95  		},
    96  		{
    97  			name:    "no match",
    98  			pattern: `(?P<key>[^1-9]+)`,
    99  			input:   "2345",
   100  			want:    nil,
   101  			wantErr: require.NoError,
   102  		},
   103  		{
   104  			name:    "error empty reader",
   105  			pattern: `(?P<key>\w+)`,
   106  			input:   "",
   107  			want:    nil,
   108  			wantErr: require.NoError,
   109  		},
   110  	}
   111  	for _, tt := range tests {
   112  		t.Run(tt.name, func(t *testing.T) {
   113  			re := regexp.MustCompile(tt.pattern)
   114  			r := strings.NewReader(tt.input)
   115  			got, err := MatchNamedCaptureGroupsFromReader(re, r)
   116  			tt.wantErr(t, err)
   117  			assert.Equal(t, tt.want, got)
   118  		})
   119  	}
   120  }
   121  
   122  func TestMatchAnyFromReader(t *testing.T) {
   123  	tests := []struct {
   124  		name     string
   125  		input    string
   126  		patterns []*regexp.Regexp
   127  		want     bool
   128  		wantErr  require.ErrorAssertionFunc
   129  	}{
   130  		{
   131  			name:     "match single pattern",
   132  			input:    "hello world",
   133  			patterns: []*regexp.Regexp{regexp.MustCompile(`hello`)},
   134  			want:     true,
   135  			wantErr:  require.NoError,
   136  		},
   137  		{
   138  			name:     "match multiple patterns",
   139  			input:    "test case",
   140  			patterns: []*regexp.Regexp{regexp.MustCompile(`case`), regexp.MustCompile(`test`)},
   141  			want:     true,
   142  			wantErr:  require.NoError,
   143  		},
   144  		{
   145  			name:     "no match",
   146  			input:    "nothing here",
   147  			patterns: []*regexp.Regexp{regexp.MustCompile(`absent`)},
   148  			want:     false,
   149  			wantErr:  require.NoError,
   150  		},
   151  		{
   152  			name:     "error empty reader",
   153  			input:    "",
   154  			patterns: []*regexp.Regexp{regexp.MustCompile(`match`)},
   155  			want:     false,
   156  			wantErr:  require.NoError,
   157  		},
   158  	}
   159  	for _, tt := range tests {
   160  		t.Run(tt.name, func(t *testing.T) {
   161  			r := strings.NewReader(tt.input)
   162  			got, err := MatchAnyFromReader(r, tt.patterns...)
   163  			tt.wantErr(t, err)
   164  			assert.Equal(t, tt.want, got)
   165  		})
   166  	}
   167  }
   168  
   169  func TestProcessReaderInChunks_ChunkBoundaries(t *testing.T) {
   170  	tests := []struct {
   171  		name          string
   172  		input         string
   173  		chunkSize     int
   174  		expectedCalls []string
   175  		returnOnChunk int
   176  		wantErr       require.ErrorAssertionFunc
   177  	}{
   178  		{
   179  			name:          "go case",
   180  			input:         "123456789012345",
   181  			chunkSize:     4,
   182  			returnOnChunk: 2,
   183  			expectedCalls: []string{"1234", "345678", "789012"},
   184  			wantErr:       require.NoError,
   185  		},
   186  		{
   187  			name:          "no match",
   188  			input:         "123456789012345",
   189  			chunkSize:     4,
   190  			returnOnChunk: -1,
   191  			expectedCalls: []string{"1234", "345678", "789012", "12345"},
   192  			wantErr:       require.NoError,
   193  		},
   194  	}
   195  	for _, tt := range tests {
   196  		t.Run(tt.name, func(t *testing.T) {
   197  			var actualCalls []string
   198  			var current int
   199  			handler := func(data []byte) (bool, error) {
   200  				actualCalls = append(actualCalls, string(data))
   201  				if current == tt.returnOnChunk {
   202  					return true, nil
   203  				}
   204  				current++
   205  				return false, nil
   206  			}
   207  			r := strings.NewReader(tt.input)
   208  			got, err := processReaderInChunks(r, tt.chunkSize, handler)
   209  			tt.wantErr(t, err)
   210  			if tt.returnOnChunk == -1 {
   211  				assert.False(t, got)
   212  			} else {
   213  				assert.True(t, got)
   214  			}
   215  			assert.Equal(t, tt.expectedCalls, actualCalls)
   216  		})
   217  	}
   218  }