// Copyright 2018 The BoringSSL Authors
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

package ar

import (
	"bytes"
	"flag"
	"os"
	"path/filepath"
	"testing"
)

var testDataDir = flag.String("testdata", "testdata", "The path to the test data directory.")

type arTest struct {
	name string
	in   string
	out  map[string]string
	// allowPadding is true if the contents may have trailing newlines at end.
	// On macOS, ar calls ranlib which pads all inputs up to eight bytes with
	// newlines. Unlike ar's native padding up to two bytes, this padding is
	// included in the size field, so it is not removed when decoding.
	allowPadding bool
}

func (test *arTest) Path(file string) string {
	return filepath.Join(*testDataDir, test.name, file)
}

func removeTrailingNewlines(in []byte) []byte {
	for len(in) > 0 && in[len(in)-1] == '\n' {
		in = in[:len(in)-1]
	}
	return in
}

var arTests = []arTest{
	{
		"linux",
		"libsample.a",
		map[string]string{
			"foo.c.o":  "foo.c.o",
			"bar.cc.o": "bar.cc.o",
		},
		false,
	},
	{
		"mac",
		"libsample.a",
		map[string]string{
			"foo.c.o":  "foo.c.o",
			"bar.cc.o": "bar.cc.o",
		},
		true,
	},
	{
		"windows",
		"sample.lib",
		map[string]string{
			"CMakeFiles\\sample.dir\\foo.c.obj":  "foo.c.obj",
			"CMakeFiles\\sample.dir\\bar.cc.obj": "bar.cc.obj",
		},
		false,
	},
}

func TestAR(t *testing.T) {
	for _, test := range arTests {
		t.Run(test.name, func(t *testing.T) {
			in, err := os.Open(test.Path(test.in))
			if err != nil {
				t.Fatalf("opening input failed: %s", err)
			}
			defer in.Close()

			ret, err := ParseAR(in)
			if err != nil {
				t.Fatalf("reading input failed: %s", err)
			}

			for file, contentsPath := range test.out {
				expected, err := os.ReadFile(test.Path(contentsPath))
				if err != nil {
					t.Fatalf("error reading %s: %s", contentsPath, err)
				}
				got, ok := ret[file]
				if test.allowPadding {
					got = removeTrailingNewlines(got)
					expected = removeTrailingNewlines(got)
				}
				if !ok {
					t.Errorf("file %s missing from output", file)
				} else if !bytes.Equal(got, expected) {
					t.Errorf("contents for file %s did not match", file)
				}
			}

			for file, _ := range ret {
				if _, ok := test.out[file]; !ok {
					t.Errorf("output contained unexpected file %q", file)
				}
			}
		})
	}
}
