package executable

import (
	"debug/macho"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/anchore/syft/syft/file"
	"github.com/anchore/syft/syft/internal/unionreader"
)

func Test_machoHasEntrypoint(t *testing.T) {

	readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
		t.Helper()
		f, err := os.Open(filepath.Join("testdata/shared-info", fixture))
		require.NoError(t, err)
		return f
	}

	tests := []struct {
		name    string
		fixture string
		want    bool
	}{
		{
			name:    "shared lib",
			fixture: "bin/libhello.dylib",
			want:    false,
		},
		{
			name:    "application",
			fixture: "bin/hello_mac",
			want:    true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			f, err := macho.NewFile(readerForFixture(t, tt.fixture))
			require.NoError(t, err)
			assert.Equal(t, tt.want, machoHasEntrypoint(f))
		})
	}
}

func Test_machoHasExports(t *testing.T) {
	readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
		t.Helper()
		f, err := os.Open(filepath.Join("testdata/shared-info", fixture))
		require.NoError(t, err)
		return f
	}

	tests := []struct {
		name    string
		fixture string
		want    bool
	}{
		{
			name:    "shared lib",
			fixture: "bin/libhello.dylib",
			want:    true,
		},
		{
			name:    "application",
			fixture: "bin/hello_mac",
			want:    false,
		},
		{
			name:    "gcc-amd64-darwin-exec-debug",
			fixture: "bin/gcc-amd64-darwin-exec-debug",
			want:    false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			f, err := macho.NewFile(readerForFixture(t, tt.fixture))
			require.NoError(t, err)
			assert.Equal(t, tt.want, machoHasExports(f))
		})
	}
}

func Test_machoUniversal(t *testing.T) {
	readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
		t.Helper()
		f, err := os.Open(filepath.Join("testdata/shared-info", fixture))
		require.NoError(t, err)
		return f
	}

	tests := []struct {
		name    string
		fixture string
		want    file.Executable
	}{
		{
			name:    "universal lib",
			fixture: "bin/libhello_universal.dylib",
			want:    file.Executable{HasExports: true, HasEntrypoint: false},
		},
		{
			name:    "universal application",
			fixture: "bin/hello_mac_universal",
			want:    file.Executable{HasExports: false, HasEntrypoint: true},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var data file.Executable
			err := findMachoFeatures(&data, readerForFixture(t, tt.fixture))
			require.NoError(t, err)

			assert.Equal(t, tt.want.HasEntrypoint, data.HasEntrypoint)
			assert.Equal(t, tt.want.HasExports, data.HasExports)
		})
	}
}
