Files
llgo/internal/crosscompile/compile/libc/libc_test.go
2025-09-10 11:47:02 +08:00

778 lines
21 KiB
Go

package libc
import (
"path/filepath"
"strings"
"testing"
)
func TestGetNewlibESP32Config_LibConfig(t *testing.T) {
config := GetNewlibESP32Config()
// Test basic configuration fields
expectedName := "newlib-esp32"
if config.Name != expectedName {
t.Errorf("Expected Name '%s', got '%s'", expectedName, config.Name)
}
expectedVersion := "esp-4.3.0_20250211-patch3"
if config.Version != expectedVersion {
t.Errorf("Expected Version '%s', got '%s'", expectedVersion, config.Version)
}
expectedUrl := "https://github.com/goplus/newlib/archive/refs/tags/esp-4.3.0_20250211-patch3.tar.gz"
if config.Url != expectedUrl {
t.Errorf("Expected Url '%s', got '%s'", expectedUrl, config.Url)
}
expectedArchiveSrcDir := "newlib-esp-4.3.0_20250211-patch3"
if config.ResourceSubDir != expectedArchiveSrcDir {
t.Errorf("Expected ResourceSubDir '%s', got '%s'", expectedArchiveSrcDir, config.ResourceSubDir)
}
// Test String() method
expectedString := "newlib-esp32-esp-4.3.0_20250211-patch3"
if config.String() != expectedString {
t.Errorf("Expected String() '%s', got '%s'", expectedString, config.String())
}
}
func TestGetPicolibcConfig_LibConfig(t *testing.T) {
config := GetPicolibcConfig()
// Test basic configuration fields
expectedName := "picolibc"
if config.Name != expectedName {
t.Errorf("Expected Name '%s', got '%s'", expectedName, config.Name)
}
expectedVersion := "v0.1.0"
if config.Version != expectedVersion {
t.Errorf("Expected Version '%s', got '%s'", expectedVersion, config.Version)
}
expectedUrl := "https://github.com/goplus/picolibc/archive/refs/heads/main.zip"
if config.Url != expectedUrl {
t.Errorf("Expected Url '%s', got '%s'", expectedUrl, config.Url)
}
expectedArchiveSrcDir := "picolibc-main"
if config.ResourceSubDir != expectedArchiveSrcDir {
t.Errorf("Expected ResourceSubDir '%s', got '%s'", expectedArchiveSrcDir, config.ResourceSubDir)
}
// Test String() method
expectedString := "picolibc-v0.1.0"
if config.String() != expectedString {
t.Errorf("Expected String() '%s', got '%s'", expectedString, config.String())
}
}
func TestGetPicolibcCompileConfig(t *testing.T) {
baseDir := "/test/base"
target := "test-target"
config := GetPicolibcCompileConfig(baseDir, target)
// Test LibcCFlags
if len(config.ExportCFlags) != 2 {
t.Errorf("Expected 2 LibcCFlags, got %d", len(config.ExportCFlags))
} else {
expected := "-I" + baseDir
if config.ExportCFlags[0] != expected {
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.ExportCFlags[0])
}
expected = "-isystem" + filepath.Join(baseDir, "newlib", "libc", "include")
if config.ExportCFlags[1] != expected {
t.Errorf("Expected LibcCFlags[1] to be '%s', got '%s'", expected, config.ExportCFlags[1])
}
}
// Test Groups configuration
if len(config.Groups) != 1 {
t.Errorf("Expected 1 group, got %d", len(config.Groups))
} else {
group := config.Groups[0]
// Test output file name
expectedOutput := "libc-" + target + ".a"
if group.OutputFileName != expectedOutput {
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, group.OutputFileName)
}
// Test files list
if len(group.Files) == 0 {
t.Error("Expected non-empty files list")
} else {
// Check a few sample files
sampleFiles := []string{
filepath.Join(baseDir, "newlib", "libc", "string", "bcmp.c"),
filepath.Join(baseDir, "newlib", "libc", "string", "memcpy.c"),
filepath.Join(baseDir, "newlib", "libc", "string", "strlen.c"),
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-malloc.c"),
filepath.Join(baseDir, "newlib", "libc", "tinystdio", "printf.c"),
}
for _, sample := range sampleFiles {
found := false
for _, file := range group.Files {
if file == sample {
found = true
break
}
}
if !found {
t.Errorf("Expected file '%s' not found in group files", sample)
}
}
}
// Test CFlags
expectedCFlags := []string{
"-D_COMPILING_NEWLIB",
"-D_HAVE_ALIAS_ATTRIBUTE",
"-DTINY_STDIO",
"-DPOSIX_IO",
"-DFORMAT_DEFAULT_INTEGER",
"-D_IEEE_LIBM",
"-D__OBSOLETE_MATH_FLOAT=1",
"-D__OBSOLETE_MATH_DOUBLE=0",
"-D_WANT_IO_C99_FORMATS",
"-nostdlib",
"-I" + baseDir,
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
"-I" + filepath.Join(baseDir, "newlib", "libm", "common"),
"-I" + filepath.Join(baseDir, "newlib", "libc", "locale"),
"-I" + filepath.Join(baseDir, "newlib", "libc", "tinystdio"),
}
if len(group.CFlags) != len(expectedCFlags) {
t.Errorf("Expected %d CFlags, got %d", len(expectedCFlags), len(group.CFlags))
} else {
for i, expected := range expectedCFlags {
if group.CFlags[i] != expected {
t.Errorf("CFlags[%d] mismatch. Expected '%s', got '%s'", i, expected, group.CFlags[i])
}
}
}
// Test LDFlags and CCFlags
if len(group.LDFlags) == 0 {
t.Error("Expected non-empty LDFlags")
}
if len(group.CCFlags) == 0 {
t.Error("Expected non-empty CCFlags")
}
}
}
func TestGetPicolibcConfig_EdgeCases(t *testing.T) {
t.Run("EmptyBaseDir", func(t *testing.T) {
config := GetPicolibcCompileConfig("", "test-target")
// Check that paths are constructed correctly even with empty baseDir
expected := "-I"
if config.ExportCFlags[0] != expected {
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.ExportCFlags[0])
}
expected = "-isystem" + filepath.Join("", "newlib", "libc", "include")
if config.ExportCFlags[1] != expected {
t.Errorf("Expected LibcCFlags[1] to be '%s', got '%s'", expected, config.ExportCFlags[1])
}
})
t.Run("EmptyTarget", func(t *testing.T) {
config := GetPicolibcCompileConfig("/test/base", "")
// Check output file name formatting
expectedOutput := "libc-.a"
if config.Groups[0].OutputFileName != expectedOutput {
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, config.Groups[0].OutputFileName)
}
})
}
func TestPicolibcFileStructure(t *testing.T) {
baseDir := "/test/base"
target := "test-target"
config := GetPicolibcCompileConfig(baseDir, target)
group := config.Groups[0]
// Test that all files have .c extension (no assembly files in picolibc config)
for _, file := range group.Files {
if !strings.HasSuffix(file, ".c") {
t.Errorf("File '%s' does not have .c extension", file)
}
}
// Test that files are from expected directories
stringFiles := 0
stdlibFiles := 0
tinystdioFiles := 0
for _, file := range group.Files {
if strings.Contains(file, "/string/") {
stringFiles++
} else if strings.Contains(file, "/stdlib/") {
stdlibFiles++
} else if strings.Contains(file, "/tinystdio/") {
tinystdioFiles++
}
}
if stringFiles < 50 {
t.Errorf("Expected at least 50 string files, got %d", stringFiles)
}
if stdlibFiles < 5 {
t.Errorf("Expected at least 5 stdlib files, got %d", stdlibFiles)
}
if tinystdioFiles < 3 {
t.Errorf("Expected at least 3 tinystdio files, got %d", tinystdioFiles)
}
// Test that all files have correct base directory
for _, file := range group.Files {
if !strings.HasPrefix(file, baseDir) {
t.Errorf("File '%s' does not have expected base directory '%s'", file, baseDir)
}
}
}
func TestPicolibcCompilerFlags(t *testing.T) {
baseDir := "/test/base"
target := "test-target"
config := GetPicolibcCompileConfig(baseDir, target)
group := config.Groups[0]
// Test that required preprocessor definitions are present
requiredDefines := []string{
"-D_COMPILING_NEWLIB",
"-D_HAVE_ALIAS_ATTRIBUTE",
"-DTINY_STDIO",
"-DPOSIX_IO",
"-DFORMAT_DEFAULT_INTEGER",
"-D_IEEE_LIBM",
"-D_WANT_IO_C99_FORMATS",
}
for _, define := range requiredDefines {
found := false
for _, flag := range group.CFlags {
if flag == define {
found = true
break
}
}
if !found {
t.Errorf("Required define '%s' not found in CFlags", define)
}
}
// Test that required include paths are present
requiredIncludes := []string{
"-I" + baseDir,
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
"-I" + filepath.Join(baseDir, "newlib", "libm", "common"),
"-I" + filepath.Join(baseDir, "newlib", "libc", "locale"),
"-I" + filepath.Join(baseDir, "newlib", "libc", "tinystdio"),
}
for _, include := range requiredIncludes {
found := false
for _, flag := range group.CFlags {
if flag == include {
found = true
break
}
}
if !found {
t.Errorf("Required include '%s' not found in CFlags", include)
}
}
// Test that nostdlib is present
found := false
for _, flag := range group.CFlags {
if flag == "-nostdlib" {
found = true
break
}
}
if !found {
t.Error("Required flag '-nostdlib' not found in CFlags")
}
}
func TestGetNewlibESP32ConfigRISCV(t *testing.T) {
baseDir := "/test/base"
target := "riscv32-unknown-elf"
config := getNewlibESP32ConfigRISCV(baseDir, target)
// Test LibcCFlags
libcDir := filepath.Join(baseDir, "newlib", "libc")
expectedCFlags := []string{
"-isystem" + filepath.Join(libcDir, "include"),
"-I" + filepath.Join(baseDir, "newlib"),
"-I" + libcDir,
}
if len(config.ExportCFlags) != len(expectedCFlags) {
t.Errorf("Expected %d LibcCFlags, got %d", len(expectedCFlags), len(config.ExportCFlags))
} else {
for i, expected := range expectedCFlags {
if config.ExportCFlags[i] != expected {
t.Errorf("ExportCFlags[%d] mismatch. Expected '%s', got '%s'", i, expected, config.ExportCFlags[i])
}
}
}
// Test Groups configuration
if len(config.Groups) != 3 {
t.Errorf("Expected 3 groups, got %d", len(config.Groups))
} else {
// Group 0: libcrt0
group0 := config.Groups[0]
expectedOutput0 := "libcrt0-" + target + ".a"
if group0.OutputFileName != expectedOutput0 {
t.Errorf("Group0 OutputFileName expected '%s', got '%s'", expectedOutput0, group0.OutputFileName)
}
// Check sample files in group0
sampleFiles0 := []string{
filepath.Join(baseDir, "libgloss", "riscv", "esp", "esp_board.c"),
filepath.Join(baseDir, "libgloss", "riscv", "esp", "crt1-board.S"),
}
for _, sample := range sampleFiles0 {
found := false
for _, file := range group0.Files {
if file == sample {
found = true
break
}
}
if !found {
t.Errorf("Expected file '%s' not found in group0 files", sample)
}
}
// Group 1: libgloss
group1 := config.Groups[1]
expectedOutput1 := "libgloss-" + target + ".a"
if group1.OutputFileName != expectedOutput1 {
t.Errorf("Group1 OutputFileName expected '%s', got '%s'", expectedOutput1, group1.OutputFileName)
}
// Check sample files in group1
sampleFiles1 := []string{
filepath.Join(baseDir, "libgloss", "libnosys", "close.c"),
filepath.Join(baseDir, "libgloss", "libnosys", "sbrk.c"),
}
for _, sample := range sampleFiles1 {
found := false
for _, file := range group1.Files {
if file == sample {
found = true
break
}
}
if !found {
t.Errorf("Expected file '%s' not found in group1 files", sample)
}
}
// Group 2: libc
group2 := config.Groups[2]
expectedOutput2 := "libc-" + target + ".a"
if group2.OutputFileName != expectedOutput2 {
t.Errorf("Group2 OutputFileName expected '%s', got '%s'", expectedOutput2, group2.OutputFileName)
}
// Check sample files in group2
sampleFiles2 := []string{
filepath.Join(libcDir, "string", "memcpy.c"),
filepath.Join(libcDir, "stdlib", "malloc.c"),
}
for _, sample := range sampleFiles2 {
found := false
for _, file := range group2.Files {
if file == sample {
found = true
break
}
}
if !found {
t.Errorf("Expected file '%s' not found in group2 files", sample)
}
}
// Test CFlags for group2
expectedCFlagsGroup2 := []string{
"-DHAVE_CONFIG_H",
"-D_LIBC",
"-DHAVE_NANOSLEEP",
"-D__NO_SYSCALLS__",
// ... (other expected flags)
}
for _, expectedFlag := range expectedCFlagsGroup2 {
found := false
for _, flag := range group2.CFlags {
if flag == expectedFlag {
found = true
break
}
}
if !found {
t.Errorf("Expected flag '%s' not found in group2 CFlags", expectedFlag)
}
}
// Test LDFlags and CCFlags
if len(group0.LDFlags) == 0 {
t.Error("Expected non-empty LDFlags in group0")
}
if len(group0.CCFlags) == 0 {
t.Error("Expected non-empty CCFlags in group0")
}
}
}
func TestGetNewlibESP32ConfigXtensa(t *testing.T) {
baseDir := "/test/base"
target := "xtensa-esp32-elf"
config := getNewlibESP32ConfigXtensa(baseDir, target)
// Test LibcCFlags
libcDir := filepath.Join(baseDir, "newlib", "libc")
expectedCFlags := []string{
"-I" + filepath.Join(libcDir, "include"),
"-I" + filepath.Join(baseDir, "newlib"),
"-I" + libcDir,
}
if len(config.ExportCFlags) != len(expectedCFlags) {
t.Errorf("Expected %d LibcCFlags, got %d", len(expectedCFlags), len(config.ExportCFlags))
} else {
for i, expected := range expectedCFlags {
if config.ExportCFlags[i] != expected {
t.Errorf("ExportCFlags[%d] mismatch. Expected '%s', got '%s'", i, expected, config.ExportCFlags[i])
}
}
}
// Test Groups configuration
if len(config.Groups) != 3 {
t.Errorf("Expected 2 groups, got %d", len(config.Groups))
} else {
// Group 0: libcrt0
group0 := config.Groups[0]
expectedOutput0 := "libcrt0-" + target + ".a"
if group0.OutputFileName != expectedOutput0 {
t.Errorf("Group0 OutputFileName expected '%s', got '%s'", expectedOutput0, group0.OutputFileName)
}
// Check sample files in group0
sampleFiles0 := []string{
filepath.Join(baseDir, "libgloss", "xtensa", "clibrary_init.c"),
filepath.Join(baseDir, "libgloss", "xtensa", "crt1-boards.S"),
}
for _, sample := range sampleFiles0 {
found := false
for _, file := range group0.Files {
if file == sample {
found = true
break
}
}
if !found {
t.Errorf("Expected file '%s' not found in group0 files", sample)
}
}
// Group 1: libgloss + libc
group1 := config.Groups[1]
expectedOutput1 := "libgloss-" + target + ".a"
if group1.OutputFileName != expectedOutput1 {
t.Errorf("Group1 OutputFileName expected '%s', got '%s'", expectedOutput1, group1.OutputFileName)
}
// Check sample files in group1
sampleFiles1 := []string{
filepath.Join(baseDir, "libgloss", "libnosys", "close.c"),
}
for _, sample := range sampleFiles1 {
found := false
for _, file := range group1.Files {
if file == sample {
found = true
break
}
}
if !found {
t.Errorf("Expected file '%s' not found in group1 files", sample)
}
}
// Test CFlags for group1
expectedCFlagsGroup1 := []string{
"-D__NO_SYSCALLS__",
"-D_NO_GETUT",
"-DHAVE_CONFIG_H",
"-D_LIBC",
// ... (other expected flags)
}
for _, expectedFlag := range expectedCFlagsGroup1 {
found := false
for _, flag := range group1.CFlags {
if flag == expectedFlag {
found = true
break
}
}
if !found {
t.Errorf("Expected flag '%s' not found in group1 CFlags", expectedFlag)
}
}
// Test LDFlags and CCFlags
if len(group0.LDFlags) == 0 {
t.Error("Expected non-empty LDFlags in group0")
}
if len(group0.CCFlags) == 0 {
t.Error("Expected non-empty CCFlags in group0")
}
}
}
func TestEdgeCases(t *testing.T) {
t.Run("EmptyBaseDir_RISCV", func(t *testing.T) {
config := getNewlibESP32ConfigRISCV("", "test-target")
libcDir := filepath.Join("", "newlib", "libc")
// Check that paths are constructed correctly
expected := "-isystem" + filepath.Join(libcDir, "include")
if config.ExportCFlags[0] != expected {
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.ExportCFlags[0])
}
})
t.Run("EmptyTarget_RISCV", func(t *testing.T) {
config := getNewlibESP32ConfigRISCV("/test/base", "")
// Check output file name formatting
expectedOutput := "libcrt0-.a"
if config.Groups[0].OutputFileName != expectedOutput {
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, config.Groups[0].OutputFileName)
}
})
t.Run("EmptyBaseDir_Xtensa", func(t *testing.T) {
config := getNewlibESP32ConfigXtensa("", "test-target")
libcDir := filepath.Join("", "newlib", "libc")
// Check that paths are constructed correctly
expected := "-I" + filepath.Join(libcDir, "include")
if config.ExportCFlags[0] != expected {
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.ExportCFlags[0])
}
})
t.Run("EmptyTarget_Xtensa", func(t *testing.T) {
config := getNewlibESP32ConfigXtensa("/test/base", "")
// Check output file name formatting
expectedOutput := "libcrt0-.a"
if config.Groups[0].OutputFileName != expectedOutput {
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, config.Groups[0].OutputFileName)
}
})
}
func TestGroupConfiguration(t *testing.T) {
baseDir := "/test/base"
target := "test-target"
t.Run("RISCV_GroupCount", func(t *testing.T) {
config := getNewlibESP32ConfigRISCV(baseDir, target)
if len(config.Groups) != 3 {
t.Errorf("Expected 3 groups for RISCV, got %d", len(config.Groups))
}
})
t.Run("Xtensa_GroupCount", func(t *testing.T) {
config := getNewlibESP32ConfigXtensa(baseDir, target)
if len(config.Groups) != 3 {
t.Errorf("Expected 2 groups for Xtensa, got %d", len(config.Groups))
}
})
t.Run("RISCV_GroupNames", func(t *testing.T) {
config := getNewlibESP32ConfigRISCV(baseDir, target)
expectedNames := []string{
"libcrt0-" + target + ".a",
"libgloss-" + target + ".a",
"libc-" + target + ".a",
}
for i, group := range config.Groups {
if group.OutputFileName != expectedNames[i] {
t.Errorf("Group %d expected name '%s', got '%s'", i, expectedNames[i], group.OutputFileName)
}
}
})
t.Run("Xtensa_GroupNames", func(t *testing.T) {
config := getNewlibESP32ConfigXtensa(baseDir, target)
expectedNames := []string{
"libcrt0-" + target + ".a",
"libgloss-" + target + ".a",
}
for i, group := range config.Groups {
if i >= len(expectedNames) {
return
}
if group.OutputFileName != expectedNames[i] {
t.Errorf("Group %d expected name '%s', got '%s'", i, expectedNames[i], group.OutputFileName)
}
}
})
}
func TestCompilerFlags(t *testing.T) {
baseDir := "/test/base"
target := "test-target"
t.Run("RISCV_CFlags", func(t *testing.T) {
config := getNewlibESP32ConfigRISCV(baseDir, target)
group := config.Groups[2] // libc group
requiredFlags := []string{
"-DHAVE_CONFIG_H",
"-D_LIBC",
"-D__NO_SYSCALLS__",
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
}
for _, flag := range requiredFlags {
found := false
for _, cflag := range group.CFlags {
if cflag == flag {
found = true
break
}
}
if !found {
t.Errorf("Required flag '%s' not found in RISCV CFlags", flag)
}
}
})
t.Run("Xtensa_CFlags", func(t *testing.T) {
config := getNewlibESP32ConfigXtensa(baseDir, target)
group := config.Groups[1] // libgloss+libc group
requiredFlags := []string{
"-D__NO_SYSCALLS__",
"-DHAVE_CONFIG_H",
"-D_LIBC",
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
}
for _, flag := range requiredFlags {
found := false
for _, cflag := range group.CFlags {
if cflag == flag {
found = true
break
}
}
if !found {
t.Errorf("Required flag '%s' not found in Xtensa CFlags", flag)
}
}
})
t.Run("CommonFlags", func(t *testing.T) {
configRISCV := getNewlibESP32ConfigRISCV(baseDir, target)
configXtensa := getNewlibESP32ConfigXtensa(baseDir, target)
// Test LDFlags
expectedLDFlags := []string{
"-nostdlib",
"-ffunction-sections",
"-fdata-sections",
}
for _, group := range configRISCV.Groups {
for _, expected := range expectedLDFlags {
found := false
for _, flag := range group.LDFlags {
if flag == expected {
found = true
break
}
}
if !found {
t.Errorf("Required LDFlag '%s' not found in RISCV group", expected)
}
}
}
for _, group := range configXtensa.Groups {
for _, expected := range expectedLDFlags {
found := false
for _, flag := range group.LDFlags {
if flag == expected {
found = true
break
}
}
if !found {
t.Errorf("Required LDFlag '%s' not found in Xtensa group", expected)
}
}
}
// Test CCFlags
expectedCCFlags := []string{
"-Oz",
"-fno-builtin",
"-ffreestanding",
}
for _, group := range configRISCV.Groups {
for _, expected := range expectedCCFlags {
found := false
for _, flag := range group.CCFlags {
if flag == expected {
found = true
break
}
}
if !found {
t.Errorf("Required CCFlag '%s' not found in RISCV group", expected)
}
}
}
for _, group := range configXtensa.Groups {
for _, expected := range expectedCCFlags {
found := false
for _, flag := range group.CCFlags {
if flag == expected {
found = true
break
}
}
if !found {
t.Errorf("Required CCFlag '%s' not found in Xtensa group", expected)
}
}
}
})
}