// Copyright 2025 The zb Authors
// SPDX-License-Identifier: MIT

package macho

import (
	"bytes"
	"encoding/binary"
	"io"
	"testing"

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

type loadCommand struct {
	cmd  LoadCmd
	data []byte
}

var aarch64LoadCommands = []loadCommand{
	{
		cmd: LoadCmdSegment64,
		data: []byte{
			0x19, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
			'_', '_', 'P', 'A', 'G', 'E', 'Z', 'E',
			'R', 'O', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdSegment64,
		data: []byte{
			0x19, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00,
			'_', '_', 'T', 'E', 'X', 'T', 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			'_', '_', 't', 'e', 'x', 't', 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			'_', '_', 'T', 'E', 'X', 'T', 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x94, 0x3f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x94, 0x3f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			'_', '_', 'u', 'n', 'w', 'i', 'n', 'd',
			'_', 'i', 'n', 'f', 'o', 0x00, 0x00, 0x00,
			'_', '_', 'T', 'E', 'X', 'T', 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0xa8, 0x3f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0xa8, 0x3f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdSegment64,
		data: []byte{
			0x19, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
			'_', '_', 'L', 'I', 'N', 'K', 'E', 'D',
			'I', 'T', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0xb8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdDyldInfoOnly,
		data: []byte{
			0x22, 0x00, 0x00, 0x80, 0x30, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x40, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdSymtab,
		data: []byte{
			0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
			0x38, 0x40, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
			0x68, 0x40, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdDysymtab,
		data: []byte{
			0x0b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdLoadDylinker,
		data: []byte{
			0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
			0x0c, 0x00, 0x00, 0x00, '/', 'u', 's', 'r',
			'/', 'l', 'i', 'b', '/', 'd', 'y', 'l',
			'd', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdUUID,
		data: []byte{
			0x1b, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
			0xad, 0x61, 0x91, 0xfe, 0xfa, 0xec, 0x30, 0x4e,
			0x83, 0x6d, 0x12, 0x4b, 0xfc, 0x6b, 0x30, 0x5f,
		},
	},
	{
		cmd: LoadCmdBuildVersion,
		data: []byte{
			0x32, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x00,
			0x00, 0x03, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x03, 0x00, 0x00, 0x00, 0x03, 0x07, 0x5b, 0x04,
		},
	},
	{
		cmd: LoadCmdSourceVersion,
		data: []byte{
			0x2a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdMain,
		data: []byte{
			0x28, 0x00, 0x00, 0x80, 0x18, 0x00, 0x00, 0x00,
			0x94, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdLoadDylib,
		data: []byte{
			0x0c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
			0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
			0x05, 0x64, 0x0c, 0x05, 0x00, 0x00, 0x01, 0x00,
			'/', 'u', 's', 'r', '/', 'l', 'i', 'b',
			'/', 'l', 'i', 'b', 'S', 'y', 's', 't',
			'e', 'm', '.', 'B', '.', 'd', 'y', 'l',
			'i', 'b', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdFunctionStarts,
		data: []byte{
			0x26, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
			0x30, 0x40, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdDataInCode,
		data: []byte{
			0x29, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
			0x38, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdCodeSignature,
		data: []byte{
			0x1d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
			0xa0, 0x40, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00,
		},
	},
}

var x86_64LoadCommands = []loadCommand{
	{
		cmd: LoadCmdSegment64,
		data: []byte{
			0x19, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
			'_', '_', 'P', 'A', 'G', 'E', 'Z', 'E',
			'R', 'O', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdSegment64,
		data: []byte{
			0x19, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00,
			'_', '_', 'T', 'E', 'X', 'T', 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			'_', '_', 't', 'e', 'x', 't', 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			'_', '_', 'T', 'E', 'X', 'T', 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x90, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x90, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			'_', '_', 'u', 'n', 'w', 'i', 'n', 'd',
			'_', 'i', 'n', 'f', 'o', 0x00, 0x00, 0x00,
			'_', '_', 'T', 'E', 'X', 'T', 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0xa0, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0xa0, 0x0f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdSegment64,
		data: []byte{
			0x19, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
			'_', '_', 'L', 'I', 'N', 'K', 'E', 'D',
			'I', 'T', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdDyldInfoOnly,
		data: []byte{
			0x22, 0x00, 0x00, 0x80, 0x30, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdSymtab,
		data: []byte{
			0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
			0x38, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
			0x68, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdDysymtab,
		data: []byte{
			0x0b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
			0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdLoadDylinker,
		data: []byte{
			0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
			0x0c, 0x00, 0x00, 0x00, '/', 'u', 's', 'r',
			'/', 'l', 'i', 'b', '/', 'd', 'y', 'l',
			'd', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdUUID,
		data: []byte{
			0x1b, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
			0xd4, 0x66, 0x72, 0x5c, 0x00, 0x4b, 0x38, 0x66,
			0xaf, 0x76, 0x26, 0x17, 0x7f, 0xea, 0x20, 0xd3,
		},
	},
	{
		cmd: LoadCmdBuildVersion,
		data: []byte{
			0x32, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
			0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x00,
			0x00, 0x03, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00,
			0x03, 0x00, 0x00, 0x00, 0x03, 0x07, 0x5b, 0x04,
		},
	},
	{
		cmd: LoadCmdSourceVersion,
		data: []byte{
			0x2a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdMain,
		data: []byte{
			0x28, 0x00, 0x00, 0x80, 0x18, 0x00, 0x00, 0x00,
			0x90, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdLoadDylib,
		data: []byte{
			0x0c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
			0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
			0x05, 0x64, 0x0c, 0x05, 0x00, 0x00, 0x01, 0x00,
			'/', 'u', 's', 'r', '/', 'l', 'i', 'b',
			'/', 'l', 'i', 'b', 'S', 'y', 's', 't',
			'e', 'm', '.', 'B', '.', 'd', 'y', 'l',
			'i', 'b', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdFunctionStarts,
		data: []byte{
			0x26, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
			0x30, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
		},
	},
	{
		cmd: LoadCmdDataInCode,
		data: []byte{
			0x29, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
			0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		},
	},
}

func TestCommandReader(t *testing.T) {
	tests := []struct {
		name        string
		dataFile    string
		startOffset int64
		imageSize   int
		want        []loadCommand
	}{
		{
			name:     "AArch64",
			dataFile: "macho-program-aarch64-apple-macos",
			want:     aarch64LoadCommands,
		},
		{
			name:        "UniversalAArch64",
			dataFile:    "macho-program-universal",
			startOffset: 16384,
			imageSize:   16824,
			want:        aarch64LoadCommands,
		},
		{
			name:     "X86_64",
			dataFile: "macho-program-x86_64-apple-macos",
			want:     x86_64LoadCommands,
		},
		{
			name:        "UniversalX86_64",
			dataFile:    "macho-program-universal",
			startOffset: 4096,
			imageSize:   4248,
			want:        x86_64LoadCommands,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			data, err := openTestFile(test.dataFile, test.startOffset, test.imageSize)
			if err != nil {
				t.Fatal(err)
			}

			r := bytes.NewReader(data)
			gotHeader, err := ReadFileHeader(r)
			if err != nil {
				t.Fatal("ReadFileHeader:", err)
			}

			var gotCommands []loadCommand
			commands := gotHeader.NewCommandReader(r)
			for i := 0; commands.Next(); i++ {
				data, err := io.ReadAll(commands)
				if err != nil {
					t.Error("Read command:", err)
				}
				lc, ok := commands.Command()
				if !ok && err == nil {
					t.Errorf("commands[%d] missing type", i)
				}
				gotCommands = append(gotCommands, loadCommand{
					cmd:  lc,
					data: data,
				})

				size, ok := commands.Size()
				switch {
				case !ok && err == nil:
					t.Errorf("commands[%d] missing size", i)
				case ok && int64(len(data)) != int64(size):
					t.Errorf("command size field = %d; command read %d bytes", size, len(data))
				}
			}
			if err := commands.Err(); err != nil {
				t.Error("Commands stopped due to:", err)
			}
			if diff := cmp.Diff(test.want, gotCommands, cmp.AllowUnexported(loadCommand{})); diff != "" {
				t.Errorf("load commands (-want +got):\n%s", diff)
			}
		})
	}
}

func TestSegmentCommandUnmarshal(t *testing.T) {
	tests := []struct {
		name string
		data []byte
		want SegmentCommand
	}{
		{
			name: "PageZero",
			data: []byte{
				0x19, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
				'_', '_', 'P', 'A', 'G', 'E', 'Z', 'E',
				'R', 'O', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			},
			want: SegmentCommand{
				Command:              LoadCmdSegment64,
				RawName:              [16]byte{'_', '_', 'P', 'A', 'G', 'E', 'Z', 'E', 'R', 'O'},
				VirtualMemoryAddress: 0x0000000000000000,
				VirtualMemorySize:    0x0000000100000000,
				FileOffset:           0,
				FileSize:             0,
				MaxProtection:        0x0,
				InitProtection:       0x0,
				Flags:                0x0,
			},
		},
		{
			name: "Text",
			data: []byte{
				0x19, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00,
				'_', '_', 'T', 'E', 'X', 'T', 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
				0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
				0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				'_', '_', 't', 'e', 'x', 't', 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				'_', '_', 'T', 'E', 'X', 'T', 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x94, 0x3f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
				0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x94, 0x3f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				'_', '_', 'u', 'n', 'w', 'i', 'n', 'd',
				'_', 'i', 'n', 'f', 'o', 0x00, 0x00, 0x00,
				'_', '_', 'T', 'E', 'X', 'T', 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0xa8, 0x3f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
				0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0xa8, 0x3f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			},
			want: SegmentCommand{
				Command:              LoadCmdSegment64,
				RawName:              [16]byte{'_', '_', 'T', 'E', 'X', 'T'},
				VirtualMemoryAddress: 0x0000000100000000,
				VirtualMemorySize:    0x0000000000004000,
				FileOffset:           0,
				FileSize:             16384,
				MaxProtection:        0x00000005,
				InitProtection:       0x00000005,
				Flags:                0x0,
				Sections: []Section{
					{
						RawName:          [16]byte{'_', '_', 't', 'e', 'x', 't'},
						RawSegmentName:   [16]byte{'_', '_', 'T', 'E', 'X', 'T'},
						Address:          0x0000000100003f94,
						Size:             0x0000000000000014,
						Offset:           16276,
						Alignment:        2,
						RelocationOffset: 0,
						RelocationCount:  0,
						Flags:            0x80000400,
					},
					{
						RawName:          [16]byte{'_', '_', 'u', 'n', 'w', 'i', 'n', 'd', '_', 'i', 'n', 'f', 'o'},
						RawSegmentName:   [16]byte{'_', '_', 'T', 'E', 'X', 'T'},
						Address:          0x0000000100003fa8,
						Size:             0x0000000000000058,
						Offset:           16296,
						Alignment:        2,
						RelocationOffset: 0,
						RelocationCount:  0,
						Flags:            0x0,
					},
				},
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			got := new(SegmentCommand)
			if err := got.UnmarshalMachO(binary.LittleEndian, test.data); err != nil {
				t.Fatal("UnmarshalMachO:", err)
			}
			if diff := cmp.Diff(&test.want, got); diff != "" {
				t.Errorf("-want +got:\n%s", diff)
			}
		})
	}
}

func TestLinkeditDataCommandUnmarshal(t *testing.T) {
	tests := []struct {
		name string
		data []byte
		want LinkeditDataCommand
	}{
		{
			name: "FunctionStarts",
			data: []byte{
				0x26, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
				0x30, 0x40, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
			},
			want: LinkeditDataCommand{
				Command:    LoadCmdFunctionStarts,
				DataOffset: 16432,
				DataSize:   8,
			},
		},
		{
			name: "DataInCode",
			data: []byte{
				0x29, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
				0x38, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			},
			want: LinkeditDataCommand{
				Command:    LoadCmdDataInCode,
				DataOffset: 16440,
				DataSize:   0,
			},
		},
		{
			name: "CodeSignature",
			data: []byte{
				0x1d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
				0xa0, 0x40, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00,
			},
			want: LinkeditDataCommand{
				Command:    LoadCmdCodeSignature,
				DataOffset: 16544,
				DataSize:   280,
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			got := new(LinkeditDataCommand)
			if err := got.UnmarshalMachO(binary.LittleEndian, test.data); err != nil {
				t.Fatal("UnmarshalMachO:", err)
			}
			if diff := cmp.Diff(&test.want, got); diff != "" {
				t.Errorf("-want +got:\n%s", diff)
			}
		})
	}
}
