feat(arch/src): .SRCINFO parser + PKGBUILD fallback
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <[email protected]>
This commit is contained in:
@@ -0,0 +1,293 @@
|
||||
package src
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// srcinfoFixtures is the committed testdata tree, relative to this package
|
||||
// directory (internal/arch/src).
|
||||
const srcinfoFixtures = "../../../testdata/srcinfo"
|
||||
|
||||
func readFixture(t *testing.T, path string) string {
|
||||
t.Helper()
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read fixture %s: %v", path, err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// TestSrcInfoParseMytoolSRCINFO parses the committed mytool .SRCINFO with
|
||||
// arch x86_64 and checks every field the converter consumes.
|
||||
func TestSrcInfoParseMytoolSRCINFO(t *testing.T) {
|
||||
src := readFixture(t, filepath.Join(srcinfoFixtures, "mytool", ".SRCINFO"))
|
||||
got, err := ParseSRCINFO(src, "x86_64")
|
||||
if err != nil {
|
||||
t.Fatalf("ParseSRCINFO: %v", err)
|
||||
}
|
||||
if got.PkgBase != "mytool" {
|
||||
t.Errorf("PkgBase = %q, want %q", got.PkgBase, "mytool")
|
||||
}
|
||||
if !slices.Equal(got.PkgNames, []string{"mytool"}) {
|
||||
t.Errorf("PkgNames = %v, want [mytool]", got.PkgNames)
|
||||
}
|
||||
if got.Version != "1.0" || got.Rel != "1" {
|
||||
t.Errorf("Version/Rel = %q/%q, want %q/%q", got.Version, got.Rel, "1.0", "1")
|
||||
}
|
||||
if got.Desc != "Example custom-build package" {
|
||||
t.Errorf("Desc = %q, want %q", got.Desc, "Example custom-build package")
|
||||
}
|
||||
if got.URL != "https://example.org" {
|
||||
t.Errorf("URL = %q, want %q", got.URL, "https://example.org")
|
||||
}
|
||||
if !slices.Equal(got.Arch, []string{"x86_64"}) {
|
||||
t.Errorf("Arch = %v, want [x86_64]", got.Arch)
|
||||
}
|
||||
if !slices.Equal(got.Depends, []string{"glibc"}) {
|
||||
t.Errorf("Depends = %v, want [glibc]", got.Depends)
|
||||
}
|
||||
if !slices.Equal(got.Sources, []string{"mytool-1.0.tar.gz"}) {
|
||||
t.Errorf("Sources = %v, want [mytool-1.0.tar.gz]", got.Sources)
|
||||
}
|
||||
if !slices.Equal(got.SHA256Sums, []string{"SKIP"}) {
|
||||
t.Errorf("SHA256Sums = %v, want [SKIP]", got.SHA256Sums)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoParseMytoolPKGBUILD exercises the best-effort bash fallback on
|
||||
// the same package and expects the essential fields to agree.
|
||||
func TestSrcInfoParseMytoolPKGBUILD(t *testing.T) {
|
||||
src := readFixture(t, filepath.Join(srcinfoFixtures, "mytool", "PKGBUILD"))
|
||||
got, err := ParsePKGBUILD(src)
|
||||
if err != nil {
|
||||
t.Fatalf("ParsePKGBUILD: %v", err)
|
||||
}
|
||||
if !slices.Equal(got.PkgNames, []string{"mytool"}) {
|
||||
t.Errorf("PkgNames = %v, want [mytool]", got.PkgNames)
|
||||
}
|
||||
if got.Version != "1.0" || got.Rel != "1" {
|
||||
t.Errorf("Version/Rel = %q/%q, want %q/%q", got.Version, got.Rel, "1.0", "1")
|
||||
}
|
||||
if got.Desc != "Example custom-build package" {
|
||||
t.Errorf("Desc = %q, want %q", got.Desc, "Example custom-build package")
|
||||
}
|
||||
if got.URL != "https://example.org" {
|
||||
t.Errorf("URL = %q, want %q", got.URL, "https://example.org")
|
||||
}
|
||||
if !slices.Equal(got.Arch, []string{"x86_64"}) {
|
||||
t.Errorf("Arch = %v, want [x86_64]", got.Arch)
|
||||
}
|
||||
if !slices.Equal(got.Depends, []string{"glibc"}) {
|
||||
t.Errorf("Depends = %v, want [glibc]", got.Depends)
|
||||
}
|
||||
if !slices.Equal(got.Sources, []string{"mytool-1.0.tar.gz"}) {
|
||||
t.Errorf("Sources = %v, want [mytool-1.0.tar.gz]", got.Sources)
|
||||
}
|
||||
if !slices.Equal(got.SHA256Sums, []string{"SKIP"}) {
|
||||
t.Errorf("SHA256Sums = %v, want [SKIP]", got.SHA256Sums)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoParseSplitPKGBUILD: a split package must PARSE fine (rejection
|
||||
// happens later in the converter), yielding both pkgnames.
|
||||
func TestSrcInfoParseSplitPKGBUILD(t *testing.T) {
|
||||
src := readFixture(t, filepath.Join(srcinfoFixtures, "split", "PKGBUILD"))
|
||||
got, err := ParsePKGBUILD(src)
|
||||
if err != nil {
|
||||
t.Fatalf("ParsePKGBUILD: %v", err)
|
||||
}
|
||||
if !slices.Equal(got.PkgNames, []string{"foo", "bar"}) {
|
||||
t.Errorf("PkgNames = %v, want [foo bar]", got.PkgNames)
|
||||
}
|
||||
if got.PkgBase != "foobar" {
|
||||
t.Errorf("PkgBase = %q, want %q", got.PkgBase, "foobar")
|
||||
}
|
||||
if got.Version != "1.0" {
|
||||
t.Errorf("Version = %q, want %q", got.Version, "1.0")
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoParseVCSPKGBUILD: VCS sources must come through verbatim so the
|
||||
// converter can detect and reject them with a clear message.
|
||||
func TestSrcInfoParseVCSPKGBUILD(t *testing.T) {
|
||||
src := readFixture(t, filepath.Join(srcinfoFixtures, "vcs", "PKGBUILD"))
|
||||
got, err := ParsePKGBUILD(src)
|
||||
if err != nil {
|
||||
t.Fatalf("ParsePKGBUILD: %v", err)
|
||||
}
|
||||
if !slices.Equal(got.PkgNames, []string{"mygit"}) {
|
||||
t.Errorf("PkgNames = %v, want [mygit]", got.PkgNames)
|
||||
}
|
||||
if !slices.Contains(got.Sources, "git+https://example.org/repo.git") {
|
||||
t.Errorf("Sources = %v, want it to contain %q", got.Sources, "git+https://example.org/repo.git")
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoArchFilter feeds a .SRCINFO with arch-specific keys and checks
|
||||
// that only the requested arch's variants are folded in.
|
||||
func TestSrcInfoArchFilter(t *testing.T) {
|
||||
src := `pkgbase = demo
|
||||
pkgver = 2.0
|
||||
pkgrel = 3
|
||||
epoch = 1
|
||||
depends = bash
|
||||
depends_x86_64 = zsh
|
||||
depends_aarch64 = sh
|
||||
source_x86_64 = demo-2.0.tar.gz
|
||||
source_aarch64 = demo-2.0-aarch64.tar.gz
|
||||
sha256sums_x86_64 = SKIP
|
||||
sha256sums_aarch64 = DEADBEEF
|
||||
pkgname = demo
|
||||
`
|
||||
got, err := ParseSRCINFO(src, "x86_64")
|
||||
if err != nil {
|
||||
t.Fatalf("ParseSRCINFO(x86_64): %v", err)
|
||||
}
|
||||
if !slices.Equal(got.Depends, []string{"bash", "zsh"}) {
|
||||
t.Errorf("Depends(x86_64) = %v, want [bash zsh] (aarch64 variant must NOT leak in)", got.Depends)
|
||||
}
|
||||
if !slices.Equal(got.Sources, []string{"demo-2.0.tar.gz"}) {
|
||||
t.Errorf("Sources(x86_64) = %v, want [demo-2.0.tar.gz]", got.Sources)
|
||||
}
|
||||
if !slices.Equal(got.SHA256Sums, []string{"SKIP"}) {
|
||||
t.Errorf("SHA256Sums(x86_64) = %v, want [SKIP]", got.SHA256Sums)
|
||||
}
|
||||
if got.Version != "2.0" || got.Rel != "3" || got.Epoch != "1" {
|
||||
t.Errorf("Version/Rel/Epoch = %q/%q/%q, want %q/%q/%q", got.Version, got.Rel, got.Epoch, "2.0", "3", "1")
|
||||
}
|
||||
|
||||
got, err = ParseSRCINFO(src, "aarch64")
|
||||
if err != nil {
|
||||
t.Fatalf("ParseSRCINFO(aarch64): %v", err)
|
||||
}
|
||||
if !slices.Equal(got.Depends, []string{"bash", "sh"}) {
|
||||
t.Errorf("Depends(aarch64) = %v, want [bash sh]", got.Depends)
|
||||
}
|
||||
if !slices.Equal(got.Sources, []string{"demo-2.0-aarch64.tar.gz"}) {
|
||||
t.Errorf("Sources(aarch64) = %v, want [demo-2.0-aarch64.tar.gz]", got.Sources)
|
||||
}
|
||||
if !slices.Equal(got.SHA256Sums, []string{"DEADBEEF"}) {
|
||||
t.Errorf("SHA256Sums(aarch64) = %v, want [DEADBEEF]", got.SHA256Sums)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoMultiPkgnameSRCINFO: multiple pkgname sections must not fail
|
||||
// parsing; every name is collected, only the FIRST section's keys apply.
|
||||
func TestSrcInfoMultiPkgnameSRCINFO(t *testing.T) {
|
||||
src := `pkgbase = split
|
||||
pkgver = 1.0
|
||||
pkgrel = 1
|
||||
pkgname = foo
|
||||
depends = a
|
||||
pkgname = bar
|
||||
depends = b
|
||||
`
|
||||
got, err := ParseSRCINFO(src, "x86_64")
|
||||
if err != nil {
|
||||
t.Fatalf("ParseSRCINFO: %v", err)
|
||||
}
|
||||
if !slices.Equal(got.PkgNames, []string{"foo", "bar"}) {
|
||||
t.Errorf("PkgNames = %v, want [foo bar]", got.PkgNames)
|
||||
}
|
||||
if !slices.Equal(got.Depends, []string{"a"}) {
|
||||
t.Errorf("Depends = %v, want [a] (only the first pkgname section applies)", got.Depends)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoPKGBUILDMissingPkgver: a PKGBUILD without a usable pkgver
|
||||
// declaration is malformed and must error.
|
||||
func TestSrcInfoPKGBUILDMissingPkgver(t *testing.T) {
|
||||
src := `pkgname=foo
|
||||
pkgrel=1
|
||||
pkgdesc="no version here"
|
||||
arch=(x86_64)
|
||||
depends=('glibc')
|
||||
`
|
||||
got, err := ParsePKGBUILD(src)
|
||||
if err == nil {
|
||||
t.Fatalf("ParsePKGBUILD = %+v, want error for missing pkgver", got)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "pkgver") {
|
||||
t.Errorf("error %q does not mention pkgver", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoLoadDirPrefersSRCINFO: loading a directory uses .SRCINFO first
|
||||
// (PkgBase is only present in the .SRCINFO fixture, not the PKGBUILD).
|
||||
func TestSrcInfoLoadDirPrefersSRCINFO(t *testing.T) {
|
||||
got, err := Load(filepath.Join(srcinfoFixtures, "mytool"), "x86_64")
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if got.PkgBase != "mytool" {
|
||||
t.Errorf("PkgBase = %q, want %q (proves .SRCINFO was preferred over PKGBUILD)", got.PkgBase, "mytool")
|
||||
}
|
||||
if got.Version != "1.0" {
|
||||
t.Errorf("Version = %q, want %q", got.Version, "1.0")
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoLoadDirFallsBackToPKGBUILD: a directory with only a PKGBUILD
|
||||
// falls back to the best-effort parser.
|
||||
func TestSrcInfoLoadDirFallsBackToPKGBUILD(t *testing.T) {
|
||||
got, err := Load(filepath.Join(srcinfoFixtures, "split"), "x86_64")
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if !slices.Equal(got.PkgNames, []string{"foo", "bar"}) {
|
||||
t.Errorf("PkgNames = %v, want [foo bar]", got.PkgNames)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoLoadFilePKGBUILD: loading a file named PKGBUILD parses it as
|
||||
// such regardless of directory.
|
||||
func TestSrcInfoLoadFilePKGBUILD(t *testing.T) {
|
||||
got, err := Load(filepath.Join(srcinfoFixtures, "vcs", "PKGBUILD"), "x86_64")
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if !slices.Equal(got.PkgNames, []string{"mygit"}) {
|
||||
t.Errorf("PkgNames = %v, want [mygit]", got.PkgNames)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoLoadFileSRCINFO: loading a file named .SRCINFO parses it as
|
||||
// such.
|
||||
func TestSrcInfoLoadFileSRCINFO(t *testing.T) {
|
||||
got, err := Load(filepath.Join(srcinfoFixtures, "mytool", ".SRCINFO"), "x86_64")
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if got.PkgBase != "mytool" {
|
||||
t.Errorf("PkgBase = %q, want %q", got.PkgBase, "mytool")
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoLoadUnknownFile: a file that is neither .SRCINFO nor PKGBUILD
|
||||
// is rejected with a clear error.
|
||||
func TestSrcInfoLoadUnknownFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
other := filepath.Join(dir, "notes.txt")
|
||||
if err := os.WriteFile(other, []byte("pkgname=foo\npkgver=1\n"), 0o644); err != nil {
|
||||
t.Fatalf("WriteFile: %v", err)
|
||||
}
|
||||
_, err := Load(other, "x86_64")
|
||||
if err == nil {
|
||||
t.Fatalf("Load(%s) = nil error, want unknown-file error", other)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "unknown") {
|
||||
t.Errorf("error %q does not mention the unknown file type", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSrcInfoLoadMissingDir: a nonexistent path errors clearly.
|
||||
func TestSrcInfoLoadMissingDir(t *testing.T) {
|
||||
_, err := Load(filepath.Join(t.TempDir(), "does-not-exist"), "x86_64")
|
||||
if err == nil {
|
||||
t.Fatalf("Load(nonexistent) = nil error, want error")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user