Replace regular expressions to extract fields from a text proto with and actual parsed protobuf. Refactor TestFS into its own package, and implement StatFS. Test: m droid dist cts alllicensemetadata Test: repo forall -c 'echo -n "$REPO_PATH " && $ANDROID_BUILD_TOP/out/host/linux-x86/bin/compliance_checkmetadata . 2>&1' | fgrep -v PASS Change-Id: Icd17a6a2b6a4e2b6ffded48e964b9c9d6e4d64d6
295 lines
8.0 KiB
Go
295 lines
8.0 KiB
Go
// Copyright 2022 Google LLC
|
|
//
|
|
// 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 projectmetadata
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"android/soong/tools/compliance/testfs"
|
|
)
|
|
|
|
const (
|
|
// EMPTY represents a METADATA file with no recognized fields
|
|
EMPTY = ``
|
|
|
|
// INVALID_NAME represents a METADATA file with the wrong type of name
|
|
INVALID_NAME = `name: a library\n`
|
|
|
|
// INVALID_DESCRIPTION represents a METADATA file with the wrong type of description
|
|
INVALID_DESCRIPTION = `description: unquoted text\n`
|
|
|
|
// INVALID_VERSION represents a METADATA file with the wrong type of version
|
|
INVALID_VERSION = `third_party { version: 1 }`
|
|
|
|
// MY_LIB_1_0 represents a METADATA file for version 1.0 of mylib
|
|
MY_LIB_1_0 = `name: "mylib" description: "my library" third_party { version: "1.0" }`
|
|
|
|
// NO_NAME_0_1 represents a METADATA file with a description but no name
|
|
NO_NAME_0_1 = `description: "my library" third_party { version: "0.1" }`
|
|
)
|
|
|
|
func TestReadMetadataForProjects(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
fs *testfs.TestFS
|
|
projects []string
|
|
expectedError string
|
|
expected []pmeta
|
|
}{
|
|
{
|
|
name: "trivial",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte("name: \"Android\"\n"),
|
|
},
|
|
projects: []string{"/a"},
|
|
expected: []pmeta{{project: "/a", versionedName: "Android"}},
|
|
},
|
|
{
|
|
name: "versioned",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(MY_LIB_1_0),
|
|
},
|
|
projects: []string{"/a"},
|
|
expected: []pmeta{{project: "/a", versionedName: "mylib_v_1.0"}},
|
|
},
|
|
{
|
|
name: "versioneddesc",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(NO_NAME_0_1),
|
|
},
|
|
projects: []string{"/a"},
|
|
expected: []pmeta{{project: "/a", versionedName: "my library"}},
|
|
},
|
|
{
|
|
name: "unterminated",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte("name: \"Android\n"),
|
|
},
|
|
projects: []string{"/a"},
|
|
expectedError: `invalid character '\n' in string`,
|
|
},
|
|
{
|
|
name: "abc",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(EMPTY),
|
|
"/b/METADATA": []byte(MY_LIB_1_0),
|
|
"/c/METADATA": []byte(NO_NAME_0_1),
|
|
},
|
|
projects: []string{"/a", "/b", "/c"},
|
|
expected: []pmeta{
|
|
{project: "/a", versionedName: ""},
|
|
{project: "/b", versionedName: "mylib_v_1.0"},
|
|
{project: "/c", versionedName: "my library"},
|
|
},
|
|
},
|
|
{
|
|
name: "ab",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(EMPTY),
|
|
"/b/METADATA": []byte(MY_LIB_1_0),
|
|
},
|
|
projects: []string{"/a", "/b", "/c"},
|
|
expected: []pmeta{
|
|
{project: "/a", versionedName: ""},
|
|
{project: "/b", versionedName: "mylib_v_1.0"},
|
|
},
|
|
},
|
|
{
|
|
name: "ac",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(EMPTY),
|
|
"/c/METADATA": []byte(NO_NAME_0_1),
|
|
},
|
|
projects: []string{"/a", "/b", "/c"},
|
|
expected: []pmeta{
|
|
{project: "/a", versionedName: ""},
|
|
{project: "/c", versionedName: "my library"},
|
|
},
|
|
},
|
|
{
|
|
name: "bc",
|
|
fs: &testfs.TestFS{
|
|
"/b/METADATA": []byte(MY_LIB_1_0),
|
|
"/c/METADATA": []byte(NO_NAME_0_1),
|
|
},
|
|
projects: []string{"/a", "/b", "/c"},
|
|
expected: []pmeta{
|
|
{project: "/b", versionedName: "mylib_v_1.0"},
|
|
{project: "/c", versionedName: "my library"},
|
|
},
|
|
},
|
|
{
|
|
name: "wrongnametype",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(INVALID_NAME),
|
|
},
|
|
projects: []string{"/a"},
|
|
expectedError: `invalid value for string type`,
|
|
},
|
|
{
|
|
name: "wrongdescriptiontype",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(INVALID_DESCRIPTION),
|
|
},
|
|
projects: []string{"/a"},
|
|
expectedError: `invalid value for string type`,
|
|
},
|
|
{
|
|
name: "wrongversiontype",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(INVALID_VERSION),
|
|
},
|
|
projects: []string{"/a"},
|
|
expectedError: `invalid value for string type`,
|
|
},
|
|
{
|
|
name: "wrongtype",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),
|
|
},
|
|
projects: []string{"/a"},
|
|
expectedError: `invalid value for string type`,
|
|
},
|
|
{
|
|
name: "empty",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(EMPTY),
|
|
},
|
|
projects: []string{"/a"},
|
|
expected: []pmeta{{project: "/a", versionedName: ""}},
|
|
},
|
|
{
|
|
name: "emptyother",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA.bp": []byte(EMPTY),
|
|
},
|
|
projects: []string{"/a"},
|
|
},
|
|
{
|
|
name: "emptyfs",
|
|
fs: &testfs.TestFS{},
|
|
projects: []string{"/a"},
|
|
},
|
|
{
|
|
name: "override",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),
|
|
"/a/METADATA.android": []byte(MY_LIB_1_0),
|
|
},
|
|
projects: []string{"/a"},
|
|
expected: []pmeta{{project: "/a", versionedName: "mylib_v_1.0"}},
|
|
},
|
|
{
|
|
name: "enchilada",
|
|
fs: &testfs.TestFS{
|
|
"/a/METADATA": []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),
|
|
"/a/METADATA.android": []byte(EMPTY),
|
|
"/b/METADATA": []byte(MY_LIB_1_0),
|
|
"/c/METADATA": []byte(NO_NAME_0_1),
|
|
},
|
|
projects: []string{"/a", "/b", "/c"},
|
|
expected: []pmeta{
|
|
{project: "/a", versionedName: ""},
|
|
{project: "/b", versionedName: "mylib_v_1.0"},
|
|
{project: "/c", versionedName: "my library"},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ix := NewIndex(tt.fs)
|
|
pms, err := ix.MetadataForProjects(tt.projects...)
|
|
if err != nil {
|
|
if len(tt.expectedError) == 0 {
|
|
t.Errorf("unexpected error: got %s, want no error", err)
|
|
} else if !strings.Contains(err.Error(), tt.expectedError) {
|
|
t.Errorf("unexpected error: got %s, want %q", err, tt.expectedError)
|
|
}
|
|
return
|
|
}
|
|
t.Logf("actual %d project metadata", len(pms))
|
|
for _, pm := range pms {
|
|
t.Logf(" %v", pm.String())
|
|
}
|
|
t.Logf("expected %d project metadata", len(tt.expected))
|
|
for _, pm := range tt.expected {
|
|
t.Logf(" %s", pm.String())
|
|
}
|
|
if len(tt.expectedError) > 0 {
|
|
t.Errorf("unexpected success: got no error, want %q err", tt.expectedError)
|
|
return
|
|
}
|
|
if len(pms) != len(tt.expected) {
|
|
t.Errorf("missing project metadata: got %d project metadata, want %d", len(pms), len(tt.expected))
|
|
}
|
|
for i := 0; i < len(pms) && i < len(tt.expected); i++ {
|
|
if msg := tt.expected[i].difference(pms[i]); msg != "" {
|
|
t.Errorf("unexpected metadata starting at index %d: %s", i, msg)
|
|
return
|
|
}
|
|
}
|
|
if len(pms) < len(tt.expected) {
|
|
t.Errorf("missing metadata starting at index %d: got nothing, want %s", len(pms), tt.expected[len(pms)].String())
|
|
}
|
|
if len(tt.expected) < len(pms) {
|
|
t.Errorf("unexpected metadata starting at index %d: got %s, want nothing", len(tt.expected), pms[len(tt.expected)].String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type pmeta struct {
|
|
project string
|
|
versionedName string
|
|
}
|
|
|
|
func (pm pmeta) String() string {
|
|
return fmt.Sprintf("project: %q versionedName: %q\n", pm.project, pm.versionedName)
|
|
}
|
|
|
|
func (pm pmeta) equals(other *ProjectMetadata) bool {
|
|
if pm.project != other.project {
|
|
return false
|
|
}
|
|
if pm.versionedName != other.VersionedName() {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (pm pmeta) difference(other *ProjectMetadata) string {
|
|
if pm.equals(other) {
|
|
return ""
|
|
}
|
|
var sb strings.Builder
|
|
fmt.Fprintf(&sb, "got")
|
|
if pm.project != other.project {
|
|
fmt.Fprintf(&sb, " project: %q", other.project)
|
|
}
|
|
if pm.versionedName != other.VersionedName() {
|
|
fmt.Fprintf(&sb, " versionedName: %q", other.VersionedName())
|
|
}
|
|
fmt.Fprintf(&sb, ", want")
|
|
if pm.project != other.project {
|
|
fmt.Fprintf(&sb, " project: %q", pm.project)
|
|
}
|
|
if pm.versionedName != other.VersionedName() {
|
|
fmt.Fprintf(&sb, " versionedName: %q", pm.versionedName)
|
|
}
|
|
return sb.String()
|
|
}
|