// Copyright 2020 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package compiler

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

	"github.com/google/go-cmp/cmp"
)

func TestConstFile(t *testing.T) {
	type arch struct {
		consts    map[string]uint64
		undefined map[string]bool
		oldFormat string
	}
	arches := map[string]arch{
		"arch1": {
			consts: map[string]uint64{
				"CONST1_ALL_DIFFERENT":   11,
				"CONST2_ALL_THE_SAME":    3,
				"CONST3_SOME_UNDEFINED":  100,
				"CONST5_SOME_UNDEFINED2": 100,
			},
			undefined: map[string]bool{
				"CONST4_ALL_UNDEFINED": true,
			},
			oldFormat: `
CONST1_ALL_DIFFERENT = 11
CONST2_ALL_THE_SAME = 3
CONST3_SOME_UNDEFINED = 100
CONST5_SOME_UNDEFINED2 = 100
# CONST4_ALL_UNDEFINED is not set
`,
		},
		"arch2": {
			consts: map[string]uint64{
				"CONST1_ALL_DIFFERENT":   22,
				"CONST2_ALL_THE_SAME":    3,
				"CONST5_SOME_UNDEFINED2": 100,
			},
			undefined: map[string]bool{
				"CONST4_ALL_UNDEFINED":  true,
				"CONST3_SOME_UNDEFINED": true,
			},
			oldFormat: `
CONST1_ALL_DIFFERENT = 22
CONST2_ALL_THE_SAME = 3
# CONST3_SOME_UNDEFINED is not set
CONST5_SOME_UNDEFINED2 = 100
# CONST4_ALL_UNDEFINED is not set
`,
		},
		"arch3": {
			consts: map[string]uint64{
				"CONST1_ALL_DIFFERENT": 33,
				"CONST2_ALL_THE_SAME":  3,
			},
			undefined: map[string]bool{
				"CONST4_ALL_UNDEFINED":   true,
				"CONST3_SOME_UNDEFINED":  true,
				"CONST5_SOME_UNDEFINED2": true,
			},
			oldFormat: `
CONST1_ALL_DIFFERENT = 33
CONST2_ALL_THE_SAME = 3
# CONST3_SOME_UNDEFINED is not set
# CONST5_SOME_UNDEFINED2 is not set
# CONST4_ALL_UNDEFINED is not set
`,
		},
	}
	const serialized = `# Code generated by syz-sysgen. DO NOT EDIT.
arches = arch1, arch2, arch3
CONST1_ALL_DIFFERENT = arch1:11, arch2:22, arch3:33
CONST2_ALL_THE_SAME = 3
CONST3_SOME_UNDEFINED = arch1:100, arch2:arch3:???
CONST4_ALL_UNDEFINED = ???
CONST5_SOME_UNDEFINED2 = 100, arch3:???
`
	cf := NewConstFile()
	for name, arch := range arches {
		cf.AddArch(name, arch.consts, arch.undefined)
	}
	data := cf.Serialize()
	if diff := cmp.Diff(serialized, string(data)); diff != "" {
		t.Fatal(diff)
	}
	{
		file, err := os.CreateTemp("", "syz-const")
		if err != nil {
			t.Fatal(err)
		}
		defer file.Close()
		defer os.Remove(file.Name())
		if _, err := file.Write(data); err != nil {
			t.Fatal(err)
		}
		file.Close()
		cf1 := DeserializeConstFile(file.Name(), nil)
		for name, arch := range arches {
			if diff := cmp.Diff(arch.consts, cf1.Arch(name)); diff != "" {
				t.Errorf("%v: %v", name, diff)
			}
		}
	}
	{
		dir := t.TempDir()
		for name, arch := range arches {
			file := filepath.Join(dir, "consts_"+name+".const")
			if err := os.WriteFile(file, []byte(arch.oldFormat), 0600); err != nil {
				t.Fatal(err)
			}
		}
		cf1 := DeserializeConstFile(filepath.Join(dir, "*"), nil)
		for name, arch := range arches {
			if diff := cmp.Diff(arch.consts, cf1.Arch(name)); diff != "" {
				t.Errorf("%v: %v", name, diff)
			}
		}
	}
}
