From 776ad80e686429a7f42c70ea66a476832a3fe5c0 Mon Sep 17 00:00:00 2001 From: Ibrahim Kanouche Date: Fri, 21 Oct 2022 20:47:42 +0000 Subject: [PATCH] Added functions to projectmetadata to retrieve additional project info Test: m droid Bug: 254901942 Change-Id: I3de4bc528bd321c76900d277295bb10709035a9c --- .../projectmetadata/projectmetadata.go | 55 ++- .../projectmetadata/projectmetadata_test.go | 464 +++++++++++++++++- 2 files changed, 498 insertions(+), 21 deletions(-) diff --git a/tools/compliance/projectmetadata/projectmetadata.go b/tools/compliance/projectmetadata/projectmetadata.go index b31413d03a..1861b471e2 100644 --- a/tools/compliance/projectmetadata/projectmetadata.go +++ b/tools/compliance/projectmetadata/projectmetadata.go @@ -40,11 +40,39 @@ type ProjectMetadata struct { project string } +// ProjectUrlMap maps url type name to url value +type ProjectUrlMap map[string]string + +// DownloadUrl returns the address of a download location +func (m ProjectUrlMap) DownloadUrl() string { + for _, urlType := range []string{"GIT", "SVN", "HG", "DARCS"} { + if url, ok := m[urlType]; ok { + return url + } + } + return "" +} + // String returns a string representation of the metadata for error messages. func (pm *ProjectMetadata) String() string { return fmt.Sprintf("project: %q\n%s", pm.project, pm.proto.String()) } +// ProjectName returns the name of the project. +func (pm *ProjectMetadata) Name() string { + return pm.proto.GetName() +} + +// ProjectVersion returns the version of the project if available. +func (pm *ProjectMetadata) Version() string { + tp := pm.proto.GetThirdParty() + if tp != nil { + version := tp.GetVersion() + return version + } + return "" +} + // VersionedName returns the name of the project including the version if any. func (pm *ProjectMetadata) VersionedName() string { name := pm.proto.GetName() @@ -65,13 +93,34 @@ func (pm *ProjectMetadata) VersionedName() string { return pm.proto.GetDescription() } +// UrlsByTypeName returns a map of URLs by Type Name +func (pm *ProjectMetadata) UrlsByTypeName() ProjectUrlMap { + tp := pm.proto.GetThirdParty() + if tp == nil { + return nil + } + if len(tp.Url) == 0 { + return nil + } + urls := make(ProjectUrlMap) + + for _, url := range tp.Url { + uri := url.GetValue() + if uri == "" { + continue + } + urls[project_metadata_proto.URL_Type_name[int32(url.GetType())]] = uri + } + return urls +} + // projectIndex describes a project to be read; after `wg.Wait()`, will contain either // a `ProjectMetadata`, pm (can be nil even without error), or a non-nil `err`. type projectIndex struct { project string - pm *ProjectMetadata - err error - done chan struct{} + pm *ProjectMetadata + err error + done chan struct{} } // finish marks the task to read the `projectIndex` completed. diff --git a/tools/compliance/projectmetadata/projectmetadata_test.go b/tools/compliance/projectmetadata/projectmetadata_test.go index 1e4256ff5f..0af0cd7306 100644 --- a/tools/compliance/projectmetadata/projectmetadata_test.go +++ b/tools/compliance/projectmetadata/projectmetadata_test.go @@ -19,6 +19,7 @@ import ( "strings" "testing" + "android/soong/compliance/project_metadata_proto" "android/soong/tools/compliance/testfs" ) @@ -40,8 +41,79 @@ const ( // 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" }` + + // URL values per type + GIT_URL = "http://example.github.com/my_lib" + SVN_URL = "http://example.svn.com/my_lib" + HG_URL = "http://example.hg.com/my_lib" + DARCS_URL = "http://example.darcs.com/my_lib" + PIPER_URL = "http://google3/third_party/my/package" + HOMEPAGE_URL = "http://example.com/homepage" + OTHER_URL = "http://google.com/" + ARCHIVE_URL = "http://ftp.example.com/" + LOCAL_SOURCE_URL = "https://android.googlesource.com/platform/external/apache-http/" ) +// libWithUrl returns a METADATA file with the right download url +func libWithUrl(urlTypes ...string) string { + var sb strings.Builder + + fmt.Fprintln(&sb, `name: "mylib" description: "my library" + third_party { + version: "1.0"`) + + for _, urltype := range urlTypes { + var urlValue string + switch urltype { + case "GIT": + urlValue = GIT_URL + case "SVN": + urlValue = SVN_URL + case "HG": + urlValue = HG_URL + case "DARCS": + urlValue = DARCS_URL + case "PIPER": + urlValue = PIPER_URL + case "HOMEPAGE": + urlValue = HOMEPAGE_URL + case "OTHER": + urlValue = OTHER_URL + case "ARCHIVE": + urlValue = ARCHIVE_URL + case "LOCAL_SOURCE": + urlValue = LOCAL_SOURCE_URL + default: + panic(fmt.Errorf("unknown url type: %q. Please update libWithUrl() in build/make/tools/compliance/projectmetadata/projectmetadata_test.go", urltype)) + } + fmt.Fprintf(&sb, " url { type: %s value: %q }\n", urltype, urlValue) + } + fmt.Fprintln(&sb, `}`) + + return sb.String() +} + +func TestVerifyAllUrlTypes(t *testing.T) { + t.Run("verifyAllUrlTypes", func(t *testing.T) { + types := make([]string, 0, len(project_metadata_proto.URL_Type_value)) + for t := range project_metadata_proto.URL_Type_value { + types = append(types, t) + } + libWithUrl(types...) + }) +} + +func TestUnknownPanics(t *testing.T) { + t.Run("Unknown panics", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("unexpected success: got no error, want panic") + } + }() + libWithUrl("SOME WILD VALUE THAT DOES NOT EXIST") + }) +} + func TestReadMetadataForProjects(t *testing.T) { tests := []struct { name string @@ -56,7 +128,13 @@ func TestReadMetadataForProjects(t *testing.T) { "/a/METADATA": []byte("name: \"Android\"\n"), }, projects: []string{"/a"}, - expected: []pmeta{{project: "/a", versionedName: "Android"}}, + expected: []pmeta{{ + project: "/a", + versionedName: "Android", + name: "Android", + version: "", + downloadUrl: "", + }}, }, { name: "versioned", @@ -64,7 +142,237 @@ func TestReadMetadataForProjects(t *testing.T) { "/a/METADATA": []byte(MY_LIB_1_0), }, projects: []string{"/a"}, - expected: []pmeta{{project: "/a", versionedName: "mylib_v_1.0"}}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }}, + }, + { + name: "lib_with_homepage", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("HOMEPAGE")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }}, + }, + { + name: "lib_with_git", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("GIT")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: GIT_URL, + }}, + }, + { + name: "lib_with_svn", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("SVN")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: SVN_URL, + }}, + }, + { + name: "lib_with_hg", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("HG")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: HG_URL, + }}, + }, + { + name: "lib_with_darcs", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("DARCS")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: DARCS_URL, + }}, + }, + { + name: "lib_with_piper", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("PIPER")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }}, + }, + { + name: "lib_with_other", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("OTHER")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }}, + }, + { + name: "lib_with_local_source", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("LOCAL_SOURCE")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }}, + }, + { + name: "lib_with_archive", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("ARCHIVE")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }}, + }, + { + name: "lib_with_all_downloads", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("DARCS", "HG", "SVN", "GIT")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: GIT_URL, + }}, + }, + { + name: "lib_with_all_downloads_in_different_order", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("DARCS", "GIT", "SVN", "HG")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: GIT_URL, + }}, + }, + { + name: "lib_with_all_but_git", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("DARCS", "HG", "SVN")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: SVN_URL, + }}, + }, + { + name: "lib_with_all_but_git_and_svn", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("DARCS", "HG")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: HG_URL, + }}, + }, + { + name: "lib_with_all_nondownloads_and_git", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("HOMEPAGE", "LOCAL_SOURCE", "PIPER", "ARCHIVE", "GIT")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: GIT_URL, + }}, + }, + { + name: "lib_with_all_nondownloads", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl("HOMEPAGE", "LOCAL_SOURCE", "PIPER", "ARCHIVE")), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }}, + }, + { + name: "lib_with_all_nondownloads", + fs: &testfs.TestFS{ + "/a/METADATA": []byte(libWithUrl()), + }, + projects: []string{"/a"}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }}, }, { name: "versioneddesc", @@ -72,7 +380,13 @@ func TestReadMetadataForProjects(t *testing.T) { "/a/METADATA": []byte(NO_NAME_0_1), }, projects: []string{"/a"}, - expected: []pmeta{{project: "/a", versionedName: "my library"}}, + expected: []pmeta{{ + project: "/a", + versionedName: "my library", + name: "", + version: "0.1", + downloadUrl: "", + }}, }, { name: "unterminated", @@ -91,9 +405,27 @@ func TestReadMetadataForProjects(t *testing.T) { }, projects: []string{"/a", "/b", "/c"}, expected: []pmeta{ - {project: "/a", versionedName: ""}, - {project: "/b", versionedName: "mylib_v_1.0"}, - {project: "/c", versionedName: "my library"}, + { + project: "/a", + versionedName: "", + name: "", + version: "", + downloadUrl: "", + }, + { + project: "/b", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }, + { + project: "/c", + versionedName: "my library", + name: "", + version: "0.1", + downloadUrl: "", + }, }, }, { @@ -104,8 +436,20 @@ func TestReadMetadataForProjects(t *testing.T) { }, projects: []string{"/a", "/b", "/c"}, expected: []pmeta{ - {project: "/a", versionedName: ""}, - {project: "/b", versionedName: "mylib_v_1.0"}, + { + project: "/a", + versionedName: "", + name: "", + version: "", + downloadUrl: "", + }, + { + project: "/b", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }, }, }, { @@ -116,8 +460,20 @@ func TestReadMetadataForProjects(t *testing.T) { }, projects: []string{"/a", "/b", "/c"}, expected: []pmeta{ - {project: "/a", versionedName: ""}, - {project: "/c", versionedName: "my library"}, + { + project: "/a", + versionedName: "", + name: "", + version: "", + downloadUrl: "", + }, + { + project: "/c", + versionedName: "my library", + name: "", + version: "0.1", + downloadUrl: "", + }, }, }, { @@ -128,8 +484,20 @@ func TestReadMetadataForProjects(t *testing.T) { }, projects: []string{"/a", "/b", "/c"}, expected: []pmeta{ - {project: "/b", versionedName: "mylib_v_1.0"}, - {project: "/c", versionedName: "my library"}, + { + project: "/b", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }, + { + project: "/c", + versionedName: "my library", + name: "", + version: "0.1", + downloadUrl: "", + }, }, }, { @@ -170,7 +538,13 @@ func TestReadMetadataForProjects(t *testing.T) { "/a/METADATA": []byte(EMPTY), }, projects: []string{"/a"}, - expected: []pmeta{{project: "/a", versionedName: ""}}, + expected: []pmeta{{ + project: "/a", + versionedName: "", + name: "", + version: "", + downloadUrl: "", + }}, }, { name: "emptyother", @@ -191,7 +565,13 @@ func TestReadMetadataForProjects(t *testing.T) { "/a/METADATA.android": []byte(MY_LIB_1_0), }, projects: []string{"/a"}, - expected: []pmeta{{project: "/a", versionedName: "mylib_v_1.0"}}, + expected: []pmeta{{ + project: "/a", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }}, }, { name: "enchilada", @@ -203,9 +583,27 @@ func TestReadMetadataForProjects(t *testing.T) { }, projects: []string{"/a", "/b", "/c"}, expected: []pmeta{ - {project: "/a", versionedName: ""}, - {project: "/b", versionedName: "mylib_v_1.0"}, - {project: "/c", versionedName: "my library"}, + { + project: "/a", + versionedName: "", + name: "", + version: "", + downloadUrl: "", + }, + { + project: "/b", + versionedName: "mylib_v_1.0", + name: "mylib", + version: "1.0", + downloadUrl: "", + }, + { + project: "/c", + versionedName: "my library", + name: "", + version: "0.1", + downloadUrl: "", + }, }, }, } @@ -255,10 +653,13 @@ func TestReadMetadataForProjects(t *testing.T) { type pmeta struct { project string versionedName string + name string + version string + downloadUrl string } func (pm pmeta) String() string { - return fmt.Sprintf("project: %q versionedName: %q\n", pm.project, pm.versionedName) + return fmt.Sprintf("project: %q versionedName: %q name: %q version: %q downloadUrl: %q\n", pm.project, pm.versionedName, pm.name, pm.version, pm.downloadUrl) } func (pm pmeta) equals(other *ProjectMetadata) bool { @@ -268,6 +669,15 @@ func (pm pmeta) equals(other *ProjectMetadata) bool { if pm.versionedName != other.VersionedName() { return false } + if pm.name != other.Name() { + return false + } + if pm.version != other.Version() { + return false + } + if pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() { + return false + } return true } @@ -283,6 +693,15 @@ func (pm pmeta) difference(other *ProjectMetadata) string { if pm.versionedName != other.VersionedName() { fmt.Fprintf(&sb, " versionedName: %q", other.VersionedName()) } + if pm.name != other.Name() { + fmt.Fprintf(&sb, " name: %q", other.Name()) + } + if pm.version != other.Version() { + fmt.Fprintf(&sb, " version: %q", other.Version()) + } + if pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() { + fmt.Fprintf(&sb, " downloadUrl: %q", other.UrlsByTypeName().DownloadUrl()) + } fmt.Fprintf(&sb, ", want") if pm.project != other.project { fmt.Fprintf(&sb, " project: %q", pm.project) @@ -290,5 +709,14 @@ func (pm pmeta) difference(other *ProjectMetadata) string { if pm.versionedName != other.VersionedName() { fmt.Fprintf(&sb, " versionedName: %q", pm.versionedName) } + if pm.name != other.Name() { + fmt.Fprintf(&sb, " name: %q", pm.name) + } + if pm.version != other.Version() { + fmt.Fprintf(&sb, " version: %q", pm.version) + } + if pm.downloadUrl != other.UrlsByTypeName().DownloadUrl() { + fmt.Fprintf(&sb, " downloadUrl: %q", pm.downloadUrl) + } return sb.String() }