// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package interfaces

import (
	"errors"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"testing"
)

// test the specific /proc/net/route path as found on Google Cloud Run instances
func TestGoogleCloudRunDefaultRouteInterface(t *testing.T) {
	dir := t.TempDir()
	savedProcNetRoutePath := procNetRoutePath
	defer func() { procNetRoutePath = savedProcNetRoutePath }()
	procNetRoutePath = filepath.Join(dir, "CloudRun")
	buf := []byte("Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\tMTU\tWindow\tIRTT\n" +
		"eth0\t8008FEA9\t00000000\t0001\t0\t0\t0\t01FFFFFF\t0\t0\t0\n" +
		"eth1\t00000000\t00000000\t0001\t0\t0\t0\t00000000\t0\t0\t0\n")
	err := os.WriteFile(procNetRoutePath, buf, 0644)
	if err != nil {
		t.Fatal(err)
	}
	got, err := DefaultRouteInterface()
	if err != nil {
		t.Fatal(err)
	}

	if got != "eth1" {
		t.Fatalf("got %s, want eth1", got)
	}
}

// we read chunks of /proc/net/route at a time, test that files longer than the chunk
// size can be handled.
func TestExtremelyLongProcNetRoute(t *testing.T) {
	dir := t.TempDir()
	savedProcNetRoutePath := procNetRoutePath
	defer func() { procNetRoutePath = savedProcNetRoutePath }()
	procNetRoutePath = filepath.Join(dir, "VeryLong")
	f, err := os.Create(procNetRoutePath)
	if err != nil {
		t.Fatal(err)
	}
	_, err = f.Write([]byte("Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\tMTU\tWindow\tIRTT\n"))
	if err != nil {
		t.Fatal(err)
	}

	for n := 0; n <= 900; n++ {
		line := fmt.Sprintf("eth%d\t8008FEA9\t00000000\t0001\t0\t0\t0\t01FFFFFF\t0\t0\t0\n", n)
		_, err := f.Write([]byte(line))
		if err != nil {
			t.Fatal(err)
		}
	}
	_, err = f.Write([]byte("tokenring1\t00000000\t00000000\t0001\t0\t0\t0\t00000000\t0\t0\t0\n"))
	if err != nil {
		t.Fatal(err)
	}

	got, err := DefaultRouteInterface()
	if err != nil {
		t.Fatal(err)
	}

	if got != "tokenring1" {
		t.Fatalf("got %q, want tokenring1", got)
	}
}

// test the specific /proc/net/route path as found on AWS App Runner instances
func TestAwsAppRunnerDefaultRouteInterface(t *testing.T) {
	dir := t.TempDir()
	savedProcNetRoutePath := procNetRoutePath
	defer func() { procNetRoutePath = savedProcNetRoutePath }()
	procNetRoutePath = filepath.Join(dir, "CloudRun")
	buf := []byte("Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\tMTU\tWindow\tIRTT\n" +
		"eth0\t00000000\tF9AFFEA9\t0003\t0\t0\t0\t00000000\t0\t0\t0\n" +
		"*\tFEA9FEA9\t00000000\t0005\t0\t0\t0\tFFFFFFFF\t0\t0\t0\n" +
		"ecs-eth0\t02AAFEA9\t01ACFEA9\t0007\t0\t0\t0\tFFFFFFFF\t0\t0\t0\n" +
		"ecs-eth0\t00ACFEA9\t00000000\t0001\t0\t0\t0\t00FFFFFF\t0\t0\t0\n" +
		"eth0\t00AFFEA9\t00000000\t0001\t0\t0\t0\t00FFFFFF\t0\t0\t0\n")
	err := os.WriteFile(procNetRoutePath, buf, 0644)
	if err != nil {
		t.Fatal(err)
	}
	got, err := DefaultRouteInterface()
	if err != nil {
		t.Fatal(err)
	}

	if got != "eth0" {
		t.Fatalf("got %s, want eth0", got)
	}
}

func BenchmarkDefaultRouteInterface(b *testing.B) {
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		if _, err := DefaultRouteInterface(); err != nil {
			b.Fatal(err)
		}
	}
}

func TestRouteLinuxNetlink(t *testing.T) {
	d, err := defaultRouteFromNetlink()
	if errors.Is(err, fs.ErrPermission) {
		t.Skip(err)
	}
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("Got: %+v", d)
}
