<commit_before>package wmi

import (
	"encoding/json"
	"fmt"
	"reflect"
	"runtime"
	"runtime/debug"
	"sync"
	"testing"
	"time"
)

func TestQuery(t *testing.T) {
	var dst []Win32_Process
	q := CreateQuery(&dst, "")
	err := Query(q, &dst)
	if err != nil {
		t.Fatal(err)
	}
}

func TestFieldMismatch(t *testing.T) {
	type s struct {
		Name        string
		HandleCount uint32
		Blah        uint32
	}
	var dst []s
	err := Query("SELECT Name, HandleCount FROM Win32_Process", &dst)
	if err == nil || err.Error() != `wmi: cannot load field "Blah" into a "uint32": no such struct field` {
		t.Error("Expected err field mismatch")
	}
}

func TestStrings(t *testing.T) {
	printed := false
	f := func() {
		var dst []Win32_Process
		zeros := 0
		q := CreateQuery(&dst, "")
		for i := 0; i < 5; i++ {
			err := Query(q, &dst)
			if err != nil {
				t.Fatal(err, q)
			}
			for _, d := range dst {
				v := reflect.ValueOf(d)
				for j := 0; j < v.NumField(); j++ {
					f := v.Field(j)
					if f.Kind() != reflect.String {
						continue
					}
					s := f.Interface().(string)
					if len(s) > 0 && s[0] == '\u0000' {
						zeros++
						if !printed {
							printed = true
							j, _ := json.MarshalIndent(&d, "", "  ")
							t.Log("Example with \\u0000:\n", string(j))
						}
					}
				}
			}
			fmt.Println("iter", i, "zeros:", zeros)
		}
		if zeros > 0 {
			t.Error("> 0 zeros")
		}
	}

	fmt.Println("Disabling GC")
	debug.SetGCPercent(-1)
	f()
	fmt.Println("Enabling GC")
	debug.SetGCPercent(100)
	f()
}

func TestNamespace(t *testing.T) {
	var dst []Win32_Process
	q := CreateQuery(&dst, "")
	err := QueryNamespace(q, &dst, `root\CIMV2`)
	if err != nil {
		t.Fatal(err)
	}
	dst = nil
	err = QueryNamespace(q, &dst, `broken\nothing`)
	if err == nil {
		t.Fatal("expected error")
	}
}

func TestCreateQuery(t *testing.T) {
	type TestStruct struct {
		Name  string
		Count int
	}
	var dst []TestStruct
	output := "SELECT Name, Count FROM TestStruct WHERE Count > 2"
	tests := []interface{}{
		&dst,
		dst,
		TestStruct{},
		&TestStruct{},
	}
	for i, test := range tests {
		if o := CreateQuery(test, "WHERE Count > 2"); o != output {
			t.Error("bad output on", i, o)
		}
	}
	if CreateQuery(3, "") != "" {
		t.Error("expected empty string")
	}
}

func _TestMany(t *testing.T) {
	limit := 5000
	fmt.Println("running until:", limit)
	fmt.Println("No panics mean it succeeded. Other errors are OK.")
	runtime.GOMAXPROCS(2)
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		for i := 0; i < limit; i++ {
			if i%25 == 0 {
				fmt.Println(i)
			}
			var dst []Win32_PerfRawData_PerfDisk_LogicalDisk
			q := CreateQuery(&dst, "")
			err := Query(q, &dst)
			if err != nil {
				fmt.Println("ERROR disk", err)
			}
		}
		wg.Done()
	}()
	go func() {
		for i := 0; i > -limit; i-- {
			if i%25 == 0 {
				fmt.Println(i)
			}
			var dst []Win32_OperatingSystem
			q := CreateQuery(&dst, "")
			err := Query(q, &dst)
			if err != nil {
				fmt.Println("ERROR OS", err)
			}
		}
		wg.Done()
	}()
	wg.Wait()
}

type Win32_Process struct {
	CSCreationClassName        string
	CSName                     string
	Caption                    string
	CommandLine                string
	CreationClassName          string
	CreationDate               time.Time
	Description                string
	ExecutablePath             string
	ExecutionState             uint16
	Handle                     string
	HandleCount                uint32
	InstallDate                time.Time
	KernelModeTime             uint64
	MaximumWorkingSetSize      uint32
	MinimumWorkingSetSize      uint32
	Name                       string
	OSCreationClassName        string
	OSName                     string
	OtherOperationCount        uint64
	OtherTransferCount         uint64
	PageFaults                 uint32
	PageFileUsage              uint32
	ParentProcessId            uint32
	PeakPageFileUsage          uint32
	PeakVirtualSize            uint64
	PeakWorkingSetSize         uint32
	Priority                   uint32
	PrivatePageCount           uint64
	ProcessId                  uint32
	QuotaNonPagedPoolUsage     uint32
	QuotaPagedPoolUsage        uint32
	QuotaPeakNonPagedPoolUsage uint32
	QuotaPeakPagedPoolUsage    uint32
	ReadOperationCount         uint64
	ReadTransferCount          uint64
	SessionId                  uint32
	Status                     string
	TerminationDate            time.Time
	ThreadCount                uint32
	UserModeTime               uint64
	VirtualSize                uint64
	WindowsVersion             string
	WorkingSetSize             uint64
	WriteOperationCount        uint64
	WriteTransferCount         uint64
}

type Win32_PerfRawData_PerfDisk_LogicalDisk struct {
	AvgDiskBytesPerRead          uint64
	AvgDiskBytesPerRead_Base     uint32
	AvgDiskBytesPerTransfer      uint64
	AvgDiskBytesPerTransfer_Base uint32
	AvgDiskBytesPerWrite         uint64
	AvgDiskBytesPerWrite_Base    uint32
	AvgDiskQueueLength           uint64
	AvgDiskReadQueueLength       uint64
	AvgDiskSecPerRead            uint32
	AvgDiskSecPerRead_Base       uint32
	AvgDiskSecPerTransfer        uint32
	AvgDiskSecPerTransfer_Base   uint32
	AvgDiskSecPerWrite           uint32
	AvgDiskSecPerWrite_Base      uint32
	AvgDiskWriteQueueLength      uint64
	Caption                      string
	CurrentDiskQueueLength       uint32
	Description                  string
	DiskBytesPerSec              uint64
	DiskReadBytesPerSec          uint64
	DiskReadsPerSec              uint32
	DiskTransfersPerSec          uint32
	DiskWriteBytesPerSec         uint64
	DiskWritesPerSec             uint32
	FreeMegabytes                uint32
	Frequency_Object             uint64
	Frequency_PerfTime           uint64
	Frequency_Sys100NS           uint64
	Name                         string
	PercentDiskReadTime          uint64
	PercentDiskReadTime_Base     uint64
	PercentDiskTime              uint64
	PercentDiskTime_Base         uint64
	PercentDiskWriteTime         uint64
	PercentDiskWriteTime_Base    uint64
	PercentFreeSpace             uint32
	PercentFreeSpace_Base        uint32
	PercentIdleTime              uint64
	PercentIdleTime_Base         uint64
	SplitIOPerSec                uint32
	Timestamp_Object             uint64
	Timestamp_PerfTime           uint64
	Timestamp_Sys100NS           uint64
}

type Win32_OperatingSystem struct {
	BootDevice                                string
	BuildNumber                               string
	BuildType                                 string
	Caption                                   string
	CodeSet                                   string
	CountryCode                               string
	CreationClassName                         string
	CSCreationClassName                       string
	CSDVersion                                string
	CSName                                    string
	CurrentTimeZone                           int16
	DataExecutionPrevention_Available         bool
	DataExecutionPrevention_32BitApplications bool
	DataExecutionPrevention_Drivers           bool
	DataExecutionPrevention_SupportPolicy     uint8
	Debug                                     bool
	Description                               string
	Distributed                               bool
	EncryptionLevel                           uint32
	ForegroundApplicationBoost                uint8
	FreePhysicalMemory                        uint64
	FreeSpaceInPagingFiles                    uint64
	FreeVirtualMemory                         uint64
	InstallDate                               time.Time
	LargeSystemCache                          uint32
	LastBootUpTime                            time.Time
	LocalDateTime                             time.Time
	Locale                                    string
	Manufacturer                              string
	MaxNumberOfProcesses                      uint32
	MaxProcessMemorySize                      uint64
	MUILanguages                              []string
	Name                                      string
	NumberOfLicensedUsers                     uint32
	NumberOfProcesses                         uint32
	NumberOfUsers                             uint32
	OperatingSystemSKU                        uint32
	Organization                              string
	OSArchitecture                            string
	OSLanguage                                uint32
	OSProductSuite                            uint32
	OSType                                    uint16
	OtherTypeDescription                      string
	PAEEnabled                                bool
	PlusProductID                             string
	PlusVersionNumber                         string
	PortableOperatingSystem                   bool
	Primary                                   bool
	ProductType                               uint32
	RegisteredUser                            string
	SerialNumber                              string
	ServicePackMajorVersion                   uint16
	ServicePackMinorVersion                   uint16
	SizeStoredInPagingFiles                   uint64
	Status                                    string
	SuiteMask                                 uint32
	SystemDevice                              string
	SystemDirectory                           string
	SystemDrive                               string
	TotalSwapSpaceSize                        uint64
	TotalVirtualMemorySize                    uint64
	TotalVisibleMemorySize                    uint64
	Version                                   string
	WindowsDirectory                          string
}
<commit_msg>Pointer these to support nils<commit_after>package wmi

import (
	"encoding/json"
	"fmt"
	"reflect"
	"runtime"
	"runtime/debug"
	"sync"
	"testing"
	"time"
)

func TestQuery(t *testing.T) {
	var dst []Win32_Process
	q := CreateQuery(&dst, "")
	err := Query(q, &dst)
	if err != nil {
		t.Fatal(err)
	}
}

func TestFieldMismatch(t *testing.T) {
	type s struct {
		Name        string
		HandleCount uint32
		Blah        uint32
	}
	var dst []s
	err := Query("SELECT Name, HandleCount FROM Win32_Process", &dst)
	if err == nil || err.Error() != `wmi: cannot load field "Blah" into a "uint32": no such struct field` {
		t.Error("Expected err field mismatch")
	}
}

func TestStrings(t *testing.T) {
	printed := false
	f := func() {
		var dst []Win32_Process
		zeros := 0
		q := CreateQuery(&dst, "")
		for i := 0; i < 5; i++ {
			err := Query(q, &dst)
			if err != nil {
				t.Fatal(err, q)
			}
			for _, d := range dst {
				v := reflect.ValueOf(d)
				for j := 0; j < v.NumField(); j++ {
					f := v.Field(j)
					if f.Kind() != reflect.String {
						continue
					}
					s := f.Interface().(string)
					if len(s) > 0 && s[0] == '\u0000' {
						zeros++
						if !printed {
							printed = true
							j, _ := json.MarshalIndent(&d, "", "  ")
							t.Log("Example with \\u0000:\n", string(j))
						}
					}
				}
			}
			fmt.Println("iter", i, "zeros:", zeros)
		}
		if zeros > 0 {
			t.Error("> 0 zeros")
		}
	}

	fmt.Println("Disabling GC")
	debug.SetGCPercent(-1)
	f()
	fmt.Println("Enabling GC")
	debug.SetGCPercent(100)
	f()
}

func TestNamespace(t *testing.T) {
	var dst []Win32_Process
	q := CreateQuery(&dst, "")
	err := QueryNamespace(q, &dst, `root\CIMV2`)
	if err != nil {
		t.Fatal(err)
	}
	dst = nil
	err = QueryNamespace(q, &dst, `broken\nothing`)
	if err == nil {
		t.Fatal("expected error")
	}
}

func TestCreateQuery(t *testing.T) {
	type TestStruct struct {
		Name  string
		Count int
	}
	var dst []TestStruct
	output := "SELECT Name, Count FROM TestStruct WHERE Count > 2"
	tests := []interface{}{
		&dst,
		dst,
		TestStruct{},
		&TestStruct{},
	}
	for i, test := range tests {
		if o := CreateQuery(test, "WHERE Count > 2"); o != output {
			t.Error("bad output on", i, o)
		}
	}
	if CreateQuery(3, "") != "" {
		t.Error("expected empty string")
	}
}

func _TestMany(t *testing.T) {
	limit := 5000
	fmt.Println("running until:", limit)
	fmt.Println("No panics mean it succeeded. Other errors are OK.")
	runtime.GOMAXPROCS(2)
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		for i := 0; i < limit; i++ {
			if i%25 == 0 {
				fmt.Println(i)
			}
			var dst []Win32_PerfRawData_PerfDisk_LogicalDisk
			q := CreateQuery(&dst, "")
			err := Query(q, &dst)
			if err != nil {
				fmt.Println("ERROR disk", err)
			}
		}
		wg.Done()
	}()
	go func() {
		for i := 0; i > -limit; i-- {
			if i%25 == 0 {
				fmt.Println(i)
			}
			var dst []Win32_OperatingSystem
			q := CreateQuery(&dst, "")
			err := Query(q, &dst)
			if err != nil {
				fmt.Println("ERROR OS", err)
			}
		}
		wg.Done()
	}()
	wg.Wait()
}

type Win32_Process struct {
	CSCreationClassName        string
	CSName                     string
	Caption                    string
	CommandLine                *string
	CreationClassName          string
	CreationDate               *time.Time
	Description                string
	ExecutablePath             *string
	ExecutionState             *uint16
	Handle                     string
	HandleCount                uint32
	InstallDate                *time.Time
	KernelModeTime             uint64
	MaximumWorkingSetSize      *uint32
	MinimumWorkingSetSize      *uint32
	Name                       string
	OSCreationClassName        string
	OSName                     string
	OtherOperationCount        uint64
	OtherTransferCount         uint64
	PageFaults                 uint32
	PageFileUsage              uint32
	ParentProcessId            uint32
	PeakPageFileUsage          uint32
	PeakVirtualSize            uint64
	PeakWorkingSetSize         uint32
	Priority                   uint32
	PrivatePageCount           uint64
	ProcessId                  uint32
	QuotaNonPagedPoolUsage     uint32
	QuotaPagedPoolUsage        uint32
	QuotaPeakNonPagedPoolUsage uint32
	QuotaPeakPagedPoolUsage    uint32
	ReadOperationCount         uint64
	ReadTransferCount          uint64
	SessionId                  uint32
	Status                     *string
	TerminationDate            *time.Time
	ThreadCount                uint32
	UserModeTime               uint64
	VirtualSize                uint64
	WindowsVersion             string
	WorkingSetSize             uint64
	WriteOperationCount        uint64
	WriteTransferCount         uint64
}

type Win32_PerfRawData_PerfDisk_LogicalDisk struct {
	AvgDiskBytesPerRead          uint64
	AvgDiskBytesPerRead_Base     uint32
	AvgDiskBytesPerTransfer      uint64
	AvgDiskBytesPerTransfer_Base uint32
	AvgDiskBytesPerWrite         uint64
	AvgDiskBytesPerWrite_Base    uint32
	AvgDiskQueueLength           uint64
	AvgDiskReadQueueLength       uint64
	AvgDiskSecPerRead            uint32
	AvgDiskSecPerRead_Base       uint32
	AvgDiskSecPerTransfer        uint32
	AvgDiskSecPerTransfer_Base   uint32
	AvgDiskSecPerWrite           uint32
	AvgDiskSecPerWrite_Base      uint32
	AvgDiskWriteQueueLength      uint64
	Caption                      string
	CurrentDiskQueueLength       uint32
	Description                  string
	DiskBytesPerSec              uint64
	DiskReadBytesPerSec          uint64
	DiskReadsPerSec              uint32
	DiskTransfersPerSec          uint32
	DiskWriteBytesPerSec         uint64
	DiskWritesPerSec             uint32
	FreeMegabytes                uint32
	Frequency_Object             uint64
	Frequency_PerfTime           uint64
	Frequency_Sys100NS           uint64
	Name                         string
	PercentDiskReadTime          uint64
	PercentDiskReadTime_Base     uint64
	PercentDiskTime              uint64
	PercentDiskTime_Base         uint64
	PercentDiskWriteTime         uint64
	PercentDiskWriteTime_Base    uint64
	PercentFreeSpace             uint32
	PercentFreeSpace_Base        uint32
	PercentIdleTime              uint64
	PercentIdleTime_Base         uint64
	SplitIOPerSec                uint32
	Timestamp_Object             uint64
	Timestamp_PerfTime           uint64
	Timestamp_Sys100NS           uint64
}

type Win32_OperatingSystem struct {
	BootDevice                                string
	BuildNumber                               string
	BuildType                                 string
	Caption                                   string
	CodeSet                                   string
	CountryCode                               string
	CreationClassName                         string
	CSCreationClassName                       string
	CSDVersion                                string
	CSName                                    string
	CurrentTimeZone                           int16
	DataExecutionPrevention_Available         bool
	DataExecutionPrevention_32BitApplications bool
	DataExecutionPrevention_Drivers           bool
	DataExecutionPrevention_SupportPolicy     uint8
	Debug                                     bool
	Description                               string
	Distributed                               bool
	EncryptionLevel                           uint32
	ForegroundApplicationBoost                uint8
	FreePhysicalMemory                        uint64
	FreeSpaceInPagingFiles                    uint64
	FreeVirtualMemory                         uint64
	InstallDate                               time.Time
	LargeSystemCache                          uint32
	LastBootUpTime                            time.Time
	LocalDateTime                             time.Time
	Locale                                    string
	Manufacturer                              string
	MaxNumberOfProcesses                      uint32
	MaxProcessMemorySize                      uint64
	MUILanguages                              []string
	Name                                      string
	NumberOfLicensedUsers                     uint32
	NumberOfProcesses                         uint32
	NumberOfUsers                             uint32
	OperatingSystemSKU                        uint32
	Organization                              string
	OSArchitecture                            string
	OSLanguage                                uint32
	OSProductSuite                            uint32
	OSType                                    uint16
	OtherTypeDescription                      string
	PAEEnabled                                bool
	PlusProductID                             string
	PlusVersionNumber                         string
	PortableOperatingSystem                   bool
	Primary                                   bool
	ProductType                               uint32
	RegisteredUser                            string
	SerialNumber                              string
	ServicePackMajorVersion                   uint16
	ServicePackMinorVersion                   uint16
	SizeStoredInPagingFiles                   uint64
	Status                                    string
	SuiteMask                                 uint32
	SystemDevice                              string
	SystemDirectory                           string
	SystemDrive                               string
	TotalSwapSpaceSize                        uint64
	TotalVirtualMemorySize                    uint64
	TotalVisibleMemorySize                    uint64
	Version                                   string
	WindowsDirectory                          string
}
<|endoftext|>


<commit_before>package stellarbase

import (
	. "github.com/smartystreets/goconvey/convey"
	"github.com/stellar/go-stellar-base/xdr"
	"testing"
)

func TestPrice(t *testing.T) {

	Convey("A price can be constructed", t, func() {
		price := xdr.Price{1, 10}

		So(price.N, ShouldEqual, 1)
		So(price.D, ShouldEqual, 10)
	})
}

func TestCurrency(t *testing.T) {

	Convey("A native currency can be constructed", t, func() {
		currency := xdr.NewCurrencyNative()
		So(currency.Type(), ShouldEqual, xdr.CurrencyTypeNative)
	})

	Convey("Given an IsoCurrencyIssuer", t, func() {
		currencyCode := [4]byte{'u', 's', 'd', 0x00}
		accountID := [32]byte{}
		isoci := xdr.IsoCurrencyIssuer{currencyCode, accountID}

		Convey("An iso currency can be constructed", func() {
			currency := xdr.NewCurrencyIso4217(isoci)
			So(currency.Type(), ShouldEqual, xdr.CurrencyTypeIso4217)
			So(currency.IsoCi(), ShouldResemble, isoci)
		})
	})

}

func TestLowLevelPayment(t *testing.T) {
	SkipConvey("A payment can be constructed", t, func() {
		seed := "s3Fy8h5LEcYVE8aofthKWHeJpygbntw5HgcekFw93K6XqTW4gEx"
		pub, priv, err := GenerateKeyFromSeed(seed)
		So(err, ShouldBeNil)
		_, _ = pub, priv

		currency := xdr.NewCurrencyNative()
		accountId := xdr.AccountId(pub.KeyData())

		pOp := xdr.PaymentOp{
			Destination: accountId,
			Currency:    currency,
			Amount:      100000000,
			SendMax:     100000000,
		}

		opBody := xdr.NewOperationBodyPayment(pOp)

		p := xdr.Operation{Body: opBody}

		tx := xdr.Transaction{
			Account:    accountId,
			MaxFee:     10,
			SeqNum:     1,
			MinLedger:  1,
			MaxLedger:  1000,
			Operations: []xdr.Operation{p},
		}

		_ = tx
	})

}
<commit_msg>Remove xdr test until we fix xdrgen<commit_after><|endoftext|>


<commit_before>package zfs

import (
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"
)

type VersionType string

const (
	Bookmark VersionType = "bookmark"
	Snapshot             = "snapshot"
)

type FilesystemVersion struct {
	Type VersionType

	// Display name. Should not be used for identification, only for user output
	Name string

	// GUID as exported by ZFS. Uniquely identifies a snapshot across pools
	Guid uint64

	// The TXG in which the snapshot was created. For bookmarks,
	// this is the GUID of the snapshot it was initially tied to.
	CreateTXG uint64
}

type fsbyCreateTXG []FilesystemVersion

func (l fsbyCreateTXG) Len() int      { return len(l) }
func (l fsbyCreateTXG) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l fsbyCreateTXG) Less(i, j int) bool {
	return l[i].CreateTXG < l[j].CreateTXG
}

/* The sender (left) wants to know if the receiver (right) has more recent versions

	Left :         | C |
	Right: | A | B | C | D | E |
	=>   :         | C | D | E |

	Left:         | C |
	Right:			  | D | E |
	=>   :  <empty list>, no common ancestor

	Left :         | C | D | E |
	Right: | A | B | C |
	=>   :  <empty list>, the left has newer versions

	Left : | A | B | C |       | F |
	Right:         | C | D | E |
	=>   :         | C |	   | F | => diverged => <empty list>

IMPORTANT: since ZFS currently does not export dataset UUIDs, the best heuristic to
		   identify a filesystem version is the tuple (name,creation)
*/
type FilesystemDiff struct {

	// The increments required to get left up to right's most recent version
	// 0th element is the common ancestor, ordered by birthtime, oldest first
	// If empty, left and right are at same most recent version
	// If nil, there is no incremental path for left to get to right's most recent version
	// This means either (check Diverged field to determine which case we are in)
	//   a) no common ancestor (left deleted all the snapshots it previously transferred to right)
	//		=> consult MRCAPathRight and request initial retransfer after prep on left side
	//   b) divergence bewteen left and right (left made snapshots that right doesn't have)
	//   	=> check MRCAPathLeft and MRCAPathRight and decide what to do based on that
	IncrementalPath []FilesystemVersion

	// true if left and right diverged, false otherwise
	Diverged bool
	// If Diverged, contains path from left most recent common ancestor (mrca)
	// to most recent version on left
	// Otherwise: nil
	MRCAPathLeft []FilesystemVersion
	// If  Diverged, contains path from right most recent common ancestor (mrca)
	// to most recent version on right
	// If there is no common ancestor (i.e. not diverged), contains entire list of
	// versions on right
	MRCAPathRight []FilesystemVersion
}

func ZFSListFilesystemVersions(fs DatasetPath) (res []FilesystemVersion, err error) {
	var fieldLines [][]string
	fieldLines, err = ZFSList(
		[]string{"name", "guid", "createtxg"},
		"-r", "-d", "1",
		"-t", "bookmark,snapshot",
		"-s", "createtxg", fs.ToString())
	if err != nil {
		return
	}
	res = make([]FilesystemVersion, len(fieldLines))
	for i, line := range fieldLines {

		if len(line[0]) < 3 {
			err = errors.New(fmt.Sprintf("snapshot or bookmark name implausibly short: %s", line[0]))
			return
		}

		snapSplit := strings.SplitN(line[0], "@", 2)
		bookmarkSplit := strings.SplitN(line[0], "#", 2)
		if len(snapSplit)*len(bookmarkSplit) != 2 {
			err = errors.New(fmt.Sprintf("dataset cannot be snapshot and bookmark at the same time: %s", line[0]))
			return
		}

		var v FilesystemVersion
		if len(snapSplit) == 2 {
			v.Name = snapSplit[1]
			v.Type = Snapshot
		} else {
			v.Name = bookmarkSplit[1]
			v.Type = Bookmark
		}

		if v.Guid, err = strconv.ParseUint(line[1], 10, 64); err != nil {
			err = errors.New(fmt.Sprintf("cannot parse GUID: %s", err.Error()))
			return
		}

		if v.CreateTXG, err = strconv.ParseUint(line[2], 10, 64); err != nil {
			err = errors.New(fmt.Sprintf("cannot parse CreateTXG: %s", err.Error()))
			return
		}

		res[i] = v

	}
	return
}

// we must assume left and right are ordered ascendingly by ZFS_PROP_CREATETXG and that
// names are unique (bas ZFS_PROP_GUID replacement)
func MakeFilesystemDiff(left, right []FilesystemVersion) (diff FilesystemDiff) {

	if right == nil {
		panic("right must not be nil")
	}
	if left == nil { // treat like no common ancestor
		diff = FilesystemDiff{
			IncrementalPath: nil,
			Diverged:        false,
			MRCAPathRight:   right,
		}
	}

	// Assert both left and right are sorted by createtxg
	var leftSorted, rightSorted fsbyCreateTXG
	leftSorted = left
	rightSorted = right
	if !sort.IsSorted(leftSorted) {
		panic("cannot make filesystem diff: unsorted left")
	}
	if !sort.IsSorted(rightSorted) {
		panic("cannot make filesystem diff: unsorted right")
	}

	// Find most recent common ancestor by name, preferring snapshots over bookmars
	mrcaLeft := len(left) - 1
	var mrcaRight int
outer:
	for ; mrcaLeft >= 0; mrcaLeft-- {
		for i := len(right) - 1; i >= 0; i-- {
			if left[mrcaLeft].Guid == right[i].Guid {
				mrcaRight = i
				if i-1 >= 0 && right[i-1].Guid == right[i].Guid && right[i-1].Type == Snapshot {
					// prefer snapshots over bookmarks
					mrcaRight = i - 1
				}
				break outer
			}
		}
	}

	// no common ancestor?
	if mrcaLeft == -1 {
		diff = FilesystemDiff{
			IncrementalPath: nil,
			Diverged:        false,
			MRCAPathRight:   right,
		}
		return
	}

	// diverged?
	if mrcaLeft != len(left)-1 {
		diff = FilesystemDiff{
			IncrementalPath: nil,
			Diverged:        true,
			MRCAPathLeft:    left[mrcaLeft:],
			MRCAPathRight:   right[mrcaRight:],
		}
		return
	}

	if mrcaLeft != len(left)-1 {
		panic("invariant violated: mrca on left must be the last item in the left list")
	}

	// strip bookmarks going forward from right
	incPath := make([]FilesystemVersion, 0, len(right))
	incPath = append(incPath, right[mrcaRight])
	// right[mrcaRight] may be a bookmark if there's no equally named snapshot
	for i := mrcaRight + 1; i < len(right); i++ {
		if right[i].Type != Bookmark {
			incPath = append(incPath, right[i])
		}
	}

	diff = FilesystemDiff{
		IncrementalPath: incPath,
	}
	return
}
<commit_msg>zfs: FilesytemDiff: len(IncrementalPath) < 2 means same most recent version<commit_after>package zfs

import (
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"
)

type VersionType string

const (
	Bookmark VersionType = "bookmark"
	Snapshot             = "snapshot"
)

type FilesystemVersion struct {
	Type VersionType

	// Display name. Should not be used for identification, only for user output
	Name string

	// GUID as exported by ZFS. Uniquely identifies a snapshot across pools
	Guid uint64

	// The TXG in which the snapshot was created. For bookmarks,
	// this is the GUID of the snapshot it was initially tied to.
	CreateTXG uint64
}

type fsbyCreateTXG []FilesystemVersion

func (l fsbyCreateTXG) Len() int      { return len(l) }
func (l fsbyCreateTXG) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l fsbyCreateTXG) Less(i, j int) bool {
	return l[i].CreateTXG < l[j].CreateTXG
}

/* The sender (left) wants to know if the receiver (right) has more recent versions

	Left :         | C |
	Right: | A | B | C | D | E |
	=>   :         | C | D | E |

	Left:         | C |
	Right:			  | D | E |
	=>   :  <empty list>, no common ancestor

	Left :         | C | D | E |
	Right: | A | B | C |
	=>   :  <empty list>, the left has newer versions

	Left : | A | B | C |       | F |
	Right:         | C | D | E |
	=>   :         | C |	   | F | => diverged => <empty list>

IMPORTANT: since ZFS currently does not export dataset UUIDs, the best heuristic to
		   identify a filesystem version is the tuple (name,creation)
*/
type FilesystemDiff struct {

	// The increments required to get left up to right's most recent version
	// 0th element is the common ancestor, ordered by birthtime, oldest first
	// If len() < 2, left and right are at same most recent version
	// If nil, there is no incremental path for left to get to right's most recent version
	// This means either (check Diverged field to determine which case we are in)
	//   a) no common ancestor (left deleted all the snapshots it previously transferred to right)
	//		=> consult MRCAPathRight and request initial retransfer after prep on left side
	//   b) divergence bewteen left and right (left made snapshots that right doesn't have)
	//   	=> check MRCAPathLeft and MRCAPathRight and decide what to do based on that
	IncrementalPath []FilesystemVersion

	// true if left and right diverged, false otherwise
	Diverged bool
	// If Diverged, contains path from left most recent common ancestor (mrca)
	// to most recent version on left
	// Otherwise: nil
	MRCAPathLeft []FilesystemVersion
	// If  Diverged, contains path from right most recent common ancestor (mrca)
	// to most recent version on right
	// If there is no common ancestor (i.e. not diverged), contains entire list of
	// versions on right
	MRCAPathRight []FilesystemVersion
}

func ZFSListFilesystemVersions(fs DatasetPath) (res []FilesystemVersion, err error) {
	var fieldLines [][]string
	fieldLines, err = ZFSList(
		[]string{"name", "guid", "createtxg"},
		"-r", "-d", "1",
		"-t", "bookmark,snapshot",
		"-s", "createtxg", fs.ToString())
	if err != nil {
		return
	}
	res = make([]FilesystemVersion, len(fieldLines))
	for i, line := range fieldLines {

		if len(line[0]) < 3 {
			err = errors.New(fmt.Sprintf("snapshot or bookmark name implausibly short: %s", line[0]))
			return
		}

		snapSplit := strings.SplitN(line[0], "@", 2)
		bookmarkSplit := strings.SplitN(line[0], "#", 2)
		if len(snapSplit)*len(bookmarkSplit) != 2 {
			err = errors.New(fmt.Sprintf("dataset cannot be snapshot and bookmark at the same time: %s", line[0]))
			return
		}

		var v FilesystemVersion
		if len(snapSplit) == 2 {
			v.Name = snapSplit[1]
			v.Type = Snapshot
		} else {
			v.Name = bookmarkSplit[1]
			v.Type = Bookmark
		}

		if v.Guid, err = strconv.ParseUint(line[1], 10, 64); err != nil {
			err = errors.New(fmt.Sprintf("cannot parse GUID: %s", err.Error()))
			return
		}

		if v.CreateTXG, err = strconv.ParseUint(line[2], 10, 64); err != nil {
			err = errors.New(fmt.Sprintf("cannot parse CreateTXG: %s", err.Error()))
			return
		}

		res[i] = v

	}
	return
}

// we must assume left and right are ordered ascendingly by ZFS_PROP_CREATETXG and that
// names are unique (bas ZFS_PROP_GUID replacement)
func MakeFilesystemDiff(left, right []FilesystemVersion) (diff FilesystemDiff) {

	if right == nil {
		panic("right must not be nil")
	}
	if left == nil { // treat like no common ancestor
		diff = FilesystemDiff{
			IncrementalPath: nil,
			Diverged:        false,
			MRCAPathRight:   right,
		}
	}

	// Assert both left and right are sorted by createtxg
	{
		var leftSorted, rightSorted fsbyCreateTXG
		leftSorted = left
		rightSorted = right
		if !sort.IsSorted(leftSorted) {
			panic("cannot make filesystem diff: unsorted left")
		}
		if !sort.IsSorted(rightSorted) {
			panic("cannot make filesystem diff: unsorted right")
		}
	}

	// Find most recent common ancestor by name, preferring snapshots over bookmarks
	mrcaLeft := len(left) - 1
	var mrcaRight int
outer:
	for ; mrcaLeft >= 0; mrcaLeft-- {
		for i := len(right) - 1; i >= 0; i-- {
			if left[mrcaLeft].Guid == right[i].Guid {
				mrcaRight = i
				if i-1 >= 0 && right[i-1].Guid == right[i].Guid && right[i-1].Type == Snapshot {
					// prefer snapshots over bookmarks
					mrcaRight = i - 1
				}
				break outer
			}
		}
	}

	// no common ancestor?
	if mrcaLeft == -1 {
		diff = FilesystemDiff{
			IncrementalPath: nil,
			Diverged:        false,
			MRCAPathRight:   right,
		}
		return
	}

	// diverged?
	if mrcaLeft != len(left)-1 {
		diff = FilesystemDiff{
			IncrementalPath: nil,
			Diverged:        true,
			MRCAPathLeft:    left[mrcaLeft:],
			MRCAPathRight:   right[mrcaRight:],
		}
		return
	}

	if mrcaLeft != len(left)-1 {
		panic("invariant violated: mrca on left must be the last item in the left list")
	}

	// incPath must not contain bookmarks except initial one,
	//   and only if that initial bookmark's snapshot is gone
	incPath := make([]FilesystemVersion, 0, len(right))
	incPath = append(incPath, right[mrcaRight])
	// right[mrcaRight] may be a bookmark if there's no equally named snapshot
	for i := mrcaRight + 1; i < len(right); i++ {
		if right[i].Type != Bookmark {
			incPath = append(incPath, right[i])
		}
	}

	diff = FilesystemDiff{
		IncrementalPath: incPath,
	}
	return
}
<|endoftext|>


<commit_before>package system

import "fmt"

type Job struct {
	Type   JobType
	Units  map[string]*Unit
	system *System
}

type JobType int

const (
	start JobType = iota
	stop
	isolate
)

func (sys *System) NewJob(typ JobType, names ...string) (j *Job, err error) {
	j = &Job{
		Type:   typ,
		Units:  map[string]*Unit{},
		system: sys,
	}

	for _, name := range names {
		if j.Units[name], err = sys.Get(name); err != nil {
			return nil, fmt.Errorf("Error loading %s: %s", name, err)
		}
	}

	//TODO: is stop case needed?
	switch j.Type {
	case start, isolate:
		for name, unit := range j.Units {
			if err = j.addDeps(unit); err != nil {
				return nil, fmt.Errorf("Error loading dependencies of %s: %s", name, err)
			}
		}
	}
	return
}

func (j *Job) Start() (err error) {
	ordering, err := j.Ordering()
	if err != nil {
		return
	}

	bug.Println("Ordering:", ordering)

	switch j.Type {
	case start:
		for _, u := range ordering {
			go u.Start()
		}
	case isolate:
		isolated := map[*Unit]struct{}{}

		for _, u := range ordering {
			go u.Start()
			isolated[u] = struct{}{}
		}

		for _, u := range j.system.units {
			if _, is := isolated[u]; !is {
				go u.Stop()
			}
		}
	case stop:
		for _, u := range ordering {
			go u.Stop()
		}
	}
	return
}
func (j *Job) Ordering() (ordering []*Unit, err error) {
	switch j.Type {
	case start, isolate:
		ordering, err = j.order()
	case stop: // TODO: Does stop also need a specific ordering?
		ordering = make([]*Unit, len(j.Units))
		for _, unit := range j.Units {
			ordering = append(ordering, unit)
		}
	}
	return
}

func (j *Job) order() (ordering []*Unit, err error) {
	g := &graph{
		map[*Unit]struct{}{},
		map[*Unit]struct{}{},
		map[*Unit]map[string]*Unit{},
		make([]*Unit, len(j.Units)),
	}

	ordering = g.ordering

	for name, unit := range j.Units {
		g.before[unit] = map[string]*Unit{}

		for _, depname := range unit.After() {
			bug.Println(name, " after ", depname)
			if dep, ok := j.Units[depname]; ok {
				g.before[unit][depname] = dep
			}
		}

		for _, depname := range unit.Before() {
			bug.Println(name, " before ", depname)
			if dep, ok := j.Units[depname]; ok {
				g.before[dep][name] = unit
			}
		}
	}

	if Debug {
		for name, unit := range j.Units {
			bug.Printf("Before %s : %v", name, g.before[unit])
		}
	}

	for name, unit := range j.Units {
		if err = g.traverse(unit); err != nil {
			return nil, fmt.Errorf("Dependency cycle determined:\n%s depends on %s", name, err)
		}
	}

	return
}
func (j *Job) addDeps(u *Unit) (err error) {
	for _, name := range u.Requires() {
		if _, added := j.Units[name]; added {
			continue
		}

		var dep *Unit
		if dep, err = j.system.Get(name); err != nil {
			u.Log.Printf("Error loading %s: %s", name, err)
			continue
		}

		err = j.addDeps(dep)
	}

	if err != nil {
		return ErrDepFail
	}

	for _, name := range u.Wants() {
		var dep *Unit
		if dep, err = j.system.Get(name); err != nil {
			continue
		}

		j.addDeps(dep)
	}

	return
}

type graph struct {
	ordered  map[*Unit]struct{}
	visited  map[*Unit]struct{}
	before   map[*Unit]map[string]*Unit
	ordering []*Unit
}

func (g *graph) traverse(unit *Unit) (err error) {
	for name, dep := range g.before[unit] {
		if _, has := g.ordered[dep]; has {
			return
		}

		if _, has := g.visited[dep]; has {
			return fmt.Errorf("%s", name)
		}

		g.visited[dep] = struct{}{}

		if err = g.traverse(dep); err != nil {
			return fmt.Errorf("%s\n%s depends on %s", name, name, err)
		}

		delete(g.visited, dep)
	}

	g.ordering = append(g.ordering, unit)
	g.ordered[unit] = struct{}{}

	return
}
<commit_msg>system.Job: ordering fixes<commit_after>package system

import "fmt"

type Job struct {
	Type   JobType
	Units  map[string]*Unit
	system *System
}

type JobType int

const (
	start JobType = iota
	stop
	isolate
)

func (sys *System) NewJob(typ JobType, names ...string) (j *Job, err error) {
	j = &Job{
		Type:   typ,
		Units:  map[string]*Unit{},
		system: sys,
	}

	for _, name := range names {
		if j.Units[name], err = sys.Get(name); err != nil {
			return nil, fmt.Errorf("Error loading %s: %s", name, err)
		}
	}

	//TODO: is stop case needed?
	switch j.Type {
	case start, isolate:
		for name, unit := range j.Units {
			if err = j.addDeps(unit); err != nil {
				return nil, fmt.Errorf("Error loading dependencies of %s: %s", name, err)
			}
		}
	}
	return
}

func (j *Job) Start() (err error) {
	ordering, err := j.Ordering()
	if err != nil {
		return
	}

	bug.Println("Ordering:", ordering)

	switch j.Type {
	case start:
		for _, u := range ordering {
			go u.Start()
		}
	case isolate:
		isolated := map[*Unit]struct{}{}

		for _, u := range ordering {
			go u.Start()
			isolated[u] = struct{}{}
		}

		for _, u := range j.system.units {
			if _, is := isolated[u]; !is {
				go u.Stop()
			}
		}
	case stop:
		for _, u := range ordering {
			go u.Stop()
		}
	}
	return
}
func (j *Job) Ordering() (ordering []*Unit, err error) {
	switch j.Type {
	case start, isolate:
		ordering, err = j.order()
	case stop: // TODO: Does stop also need a specific ordering?
		ordering = make([]*Unit, len(j.Units))
		for _, unit := range j.Units {
			ordering = append(ordering, unit)
		}
	}
	return
}

func (j *Job) order() (ordering []*Unit, err error) {
	g := &graph{
		map[*Unit]struct{}{},
		map[*Unit]struct{}{},
		map[*Unit]map[string]*Unit{},
		make([]*Unit, 0, len(j.Units)),
	}

	for _, unit := range j.Units {
		g.before[unit] = map[string]*Unit{}
	}

	for name, unit := range j.Units {
		for _, depname := range unit.After() {
			bug.Println(name, " after ", depname)
			if dep, ok := j.Units[depname]; ok {
				g.before[unit][depname] = dep
			}
		}

		for _, depname := range unit.Before() {
			bug.Println(name, " before ", depname)
			if dep, ok := j.Units[depname]; ok {
				g.before[dep][name] = unit
			}
		}
	}

	if Debug {
		for name, unit := range j.Units {
			bug.Printf("Before %s : %v", name, g.before[unit])
		}
	}

	for name, unit := range j.Units {
		if err = g.traverse(unit); err != nil {
			return nil, fmt.Errorf("Dependency cycle determined:\n%s depends on %s", name, err)
		}
	}

	return g.ordering, nil
}
func (j *Job) addDeps(u *Unit) (err error) {
	for _, name := range u.Requires() {
		if _, added := j.Units[name]; added {
			continue
		}

		var dep *Unit
		if dep, err = j.system.Get(name); err != nil {
			u.Log.Printf("Error loading %s: %s", name, err)
			continue
		}

		if err = j.addDeps(dep); err != nil {
			continue
		}

		j.Units[name] = dep
	}

	if err != nil {
		return ErrDepFail
	}

	for _, name := range u.Wants() {
		var dep *Unit
		if dep, err = j.system.Get(name); err != nil {
			continue
		}

		j.addDeps(dep)
	}

	return
}

type graph struct {
	ordered  map[*Unit]struct{}
	visited  map[*Unit]struct{}
	before   map[*Unit]map[string]*Unit
	ordering []*Unit
}

func (g *graph) traverse(unit *Unit) (err error) {
	for name, dep := range g.before[unit] {
		if _, has := g.ordered[dep]; has {
			return
		}

		if _, has := g.visited[dep]; has {
			return fmt.Errorf("%s", name)
		}

		g.visited[dep] = struct{}{}

		if err = g.traverse(dep); err != nil {
			return fmt.Errorf("%s\n%s depends on %s", name, name, err)
		}

		delete(g.visited, dep)
	}

	if _, has := g.ordered[unit]; !has {
		g.ordering = append(g.ordering, unit)
		g.ordered[unit] = struct{}{}
	}

	return
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package table

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/osrg/gobgp/packet"
	"net"
	"reflect"
)

type Path interface {
	String() string
	GetPathAttrs() []bgp.PathAttributeInterface
	GetPathAttr(bgp.BGPAttrType) (int, bgp.PathAttributeInterface)
	getRouteFamily() RouteFamily
	setSource(source *PeerInfo)
	getSource() *PeerInfo
	setNexthop(nexthop net.IP)
	getNexthop() net.IP
	setSourceVerNum(sourceVerNum int)
	getSourceVerNum() int
	setWithdraw(withdraw bool)
	IsWithdraw() bool
	GetNlri() bgp.AddrPrefixInterface
	getPrefix() net.IP
	setMedSetByTargetNeighbor(medSetByTargetNeighbor bool)
	getMedSetByTargetNeighbor() bool
	Clone(IsWithdraw bool) Path
	setBest(isBest bool)
	MarshalJSON() ([]byte, error)
}

type PathDefault struct {
	routeFamily            RouteFamily
	source                 *PeerInfo
	nexthop                net.IP
	sourceVerNum           int
	withdraw               bool
	nlri                   bgp.AddrPrefixInterface
	pathAttrs              []bgp.PathAttributeInterface
	medSetByTargetNeighbor bool
	isBest                 bool
}

func NewPathDefault(rf RouteFamily, source *PeerInfo, nlri bgp.AddrPrefixInterface, sourceVerNum int, nexthop net.IP, isWithdraw bool, pattrs []bgp.PathAttributeInterface, medSetByTargetNeighbor bool) *PathDefault {

	if !isWithdraw && pattrs == nil {
		log.Error("Need to provide nexthop and patattrs for path that is not a withdraw.")
		return nil
	}

	path := &PathDefault{}
	path.routeFamily = rf
	path.pathAttrs = pattrs
	path.nlri = nlri
	path.source = source
	path.nexthop = nexthop
	path.sourceVerNum = sourceVerNum
	path.withdraw = isWithdraw
	path.medSetByTargetNeighbor = medSetByTargetNeighbor
	path.isBest = false
	return path
}

func (pd *PathDefault) setBest(isBest bool) {
	pd.isBest = isBest
}

func (pd *PathDefault) MarshalJSON() ([]byte, error) {
	med := uint32(0)
	_, attr := pd.GetPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
	if attr != nil {
		med = attr.(*bgp.PathAttributeMultiExitDisc).Value
	}

	aslist := make([]uint32, 0)
	_, attr = pd.GetPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
	if attr != nil {
		for _, p := range attr.(*bgp.PathAttributeAsPath).Value {
			path := p.(*bgp.As4PathParam)
			aslist = append(aslist, path.AS...)
		}
	}

	var prefix net.IP
	var prefixLen uint8
	switch nlri := pd.nlri.(type) {
	case *bgp.NLRInfo:
		prefix = nlri.Prefix
		prefixLen = nlri.Length
	}

	return json.Marshal(struct {
		Network string
		Nexthop string
		AsPath  []uint32
		Metric  string
		//origin string
		Best string
	}{
		Network: prefix.String() + "/" + fmt.Sprint(prefixLen),
		Nexthop: pd.nexthop.String(),
		Metric:  fmt.Sprint(med),
		AsPath:  aslist,
		Best:    fmt.Sprint(pd.isBest),
	})
}

// create new PathAttributes
func (pd *PathDefault) Clone(isWithdraw bool) Path {
	copiedAttrs := []bgp.PathAttributeInterface(nil)
	nlri := pd.nlri
	if isWithdraw {
		if !pd.IsWithdraw() {
			nlri = &bgp.WithdrawnRoute{pd.nlri.(*bgp.NLRInfo).IPAddrPrefix}
		}
	} else {
		copiedAttrs = append(copiedAttrs, pd.pathAttrs...)
		for i, attr := range copiedAttrs {
			t, v := reflect.TypeOf(attr), reflect.ValueOf(attr)
			newAttrObjp := reflect.New(t.Elem())
			newAttrObjp.Elem().Set(v.Elem())
			copiedAttrs[i] = newAttrObjp.Interface().(bgp.PathAttributeInterface)
		}
	}
	return CreatePath(pd.source, nlri, copiedAttrs, isWithdraw)
}

func (pd *PathDefault) getRouteFamily() RouteFamily {
	return pd.routeFamily
}

func (pd *PathDefault) setSource(source *PeerInfo) {
	pd.source = source
}
func (pd *PathDefault) getSource() *PeerInfo {
	return pd.source
}

func (pd *PathDefault) setNexthop(nexthop net.IP) {
	pd.nexthop = nexthop
}

func (pd *PathDefault) getNexthop() net.IP {
	return pd.nexthop
}

func (pd *PathDefault) setSourceVerNum(sourceVerNum int) {
	pd.sourceVerNum = sourceVerNum
}

func (pd *PathDefault) getSourceVerNum() int {
	return pd.sourceVerNum
}

func (pd *PathDefault) setWithdraw(withdraw bool) {
	pd.withdraw = withdraw
}

func (pd *PathDefault) IsWithdraw() bool {
	return pd.withdraw
}

func (pd *PathDefault) GetNlri() bgp.AddrPrefixInterface {
	return pd.nlri
}

func (pd *PathDefault) setMedSetByTargetNeighbor(medSetByTargetNeighbor bool) {
	pd.medSetByTargetNeighbor = medSetByTargetNeighbor
}

func (pd *PathDefault) getMedSetByTargetNeighbor() bool {
	return pd.medSetByTargetNeighbor
}

func (pd *PathDefault) GetPathAttrs() []bgp.PathAttributeInterface {
	return pd.pathAttrs
}

func (pd *PathDefault) GetPathAttr(pattrType bgp.BGPAttrType) (int, bgp.PathAttributeInterface) {
	attrMap := [bgp.BGP_ATTR_TYPE_AS4_AGGREGATOR + 1]reflect.Type{}
	attrMap[bgp.BGP_ATTR_TYPE_ORIGIN] = reflect.TypeOf(&bgp.PathAttributeOrigin{})
	attrMap[bgp.BGP_ATTR_TYPE_AS_PATH] = reflect.TypeOf(&bgp.PathAttributeAsPath{})
	attrMap[bgp.BGP_ATTR_TYPE_NEXT_HOP] = reflect.TypeOf(&bgp.PathAttributeNextHop{})
	attrMap[bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC] = reflect.TypeOf(&bgp.PathAttributeMultiExitDisc{})
	attrMap[bgp.BGP_ATTR_TYPE_LOCAL_PREF] = reflect.TypeOf(&bgp.PathAttributeLocalPref{})
	attrMap[bgp.BGP_ATTR_TYPE_ATOMIC_AGGREGATE] = reflect.TypeOf(&bgp.PathAttributeAtomicAggregate{})
	attrMap[bgp.BGP_ATTR_TYPE_AGGREGATOR] = reflect.TypeOf(&bgp.PathAttributeAggregator{})
	attrMap[bgp.BGP_ATTR_TYPE_COMMUNITIES] = reflect.TypeOf(&bgp.PathAttributeCommunities{})
	attrMap[bgp.BGP_ATTR_TYPE_ORIGINATOR_ID] = reflect.TypeOf(&bgp.PathAttributeOriginatorId{})
	attrMap[bgp.BGP_ATTR_TYPE_CLUSTER_LIST] = reflect.TypeOf(&bgp.PathAttributeClusterList{})
	attrMap[bgp.BGP_ATTR_TYPE_MP_REACH_NLRI] = reflect.TypeOf(&bgp.PathAttributeMpReachNLRI{})
	attrMap[bgp.BGP_ATTR_TYPE_MP_UNREACH_NLRI] = reflect.TypeOf(&bgp.PathAttributeMpUnreachNLRI{})
	attrMap[bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES] = reflect.TypeOf(&bgp.PathAttributeExtendedCommunities{})
	attrMap[bgp.BGP_ATTR_TYPE_AS4_PATH] = reflect.TypeOf(&bgp.PathAttributeAs4Path{})
	attrMap[bgp.BGP_ATTR_TYPE_AS4_AGGREGATOR] = reflect.TypeOf(&bgp.PathAttributeAs4Aggregator{})

	t := attrMap[pattrType]
	for i, p := range pd.pathAttrs {
		if t == reflect.TypeOf(p) {
			return i, p
		}
	}
	return -1, nil
}

// return Path's string representation
func (pi *PathDefault) String() string {
	str := fmt.Sprintf("IPv4Path Source: %d, ", pi.getSourceVerNum())
	str = str + fmt.Sprintf(" NLRI: %s, ", pi.getPrefix().String())
	str = str + fmt.Sprintf(" nexthop: %s, ", pi.getNexthop().String())
	str = str + fmt.Sprintf(" withdraw: %s, ", pi.IsWithdraw())
	//str = str + fmt.Sprintf(" path attributes: %s, ", pi.getPathAttributeMap())
	return str
}

func (pi *PathDefault) getPrefix() net.IP {

	switch nlri := pi.nlri.(type) {
	case *bgp.NLRInfo:
		return nlri.Prefix
	case *bgp.WithdrawnRoute:
		return nlri.Prefix
	}
	return nil
}

// create Path object based on route family
func CreatePath(source *PeerInfo, nlri bgp.AddrPrefixInterface, attrs []bgp.PathAttributeInterface, isWithdraw bool) Path {

	rf := RouteFamily(int(nlri.AFI())<<16 | int(nlri.SAFI()))
	log.Debugf("afi: %d, safi: %d ", int(nlri.AFI()), nlri.SAFI())
	var path Path
	var sourceVerNum int = 1

	if source != nil {
		sourceVerNum = source.VersionNum
	}

	switch rf {
	case RF_IPv4_UC:
		log.Debugf("RouteFamily : %s", RF_IPv4_UC.String())
		path = NewIPv4Path(source, nlri, sourceVerNum, isWithdraw, attrs, false)
	case RF_IPv6_UC:
		log.Debugf("RouteFamily : %s", RF_IPv6_UC.String())
		path = NewIPv6Path(source, nlri, sourceVerNum, isWithdraw, attrs, false)
	}
	return path
}

/*
* 	Definition of inherited Path  interface
 */
type IPv4Path struct {
	*PathDefault
}

func NewIPv4Path(source *PeerInfo, nlri bgp.AddrPrefixInterface, sourceVerNum int, isWithdraw bool, attrs []bgp.PathAttributeInterface, medSetByTargetNeighbor bool) *IPv4Path {
	ipv4Path := &IPv4Path{}
	ipv4Path.PathDefault = NewPathDefault(RF_IPv4_UC, source, nlri, sourceVerNum, nil, isWithdraw, attrs, medSetByTargetNeighbor)
	if !isWithdraw {
		_, nexthop_attr := ipv4Path.GetPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
		ipv4Path.nexthop = nexthop_attr.(*bgp.PathAttributeNextHop).Value
	}
	return ipv4Path
}

func (ipv4p *IPv4Path) setPathDefault(pd *PathDefault) {
	ipv4p.PathDefault = pd
}
func (ipv4p *IPv4Path) getPathDefault() *PathDefault {
	return ipv4p.PathDefault
}

type IPv6Path struct {
	*PathDefault
}

func NewIPv6Path(source *PeerInfo, nlri bgp.AddrPrefixInterface, sourceVerNum int, isWithdraw bool, attrs []bgp.PathAttributeInterface, medSetByTargetNeighbor bool) *IPv6Path {
	ipv6Path := &IPv6Path{}
	ipv6Path.PathDefault = NewPathDefault(RF_IPv6_UC, source, nlri, sourceVerNum, nil, isWithdraw, attrs, medSetByTargetNeighbor)
	if !isWithdraw {
		_, mpattr := ipv6Path.GetPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
		ipv6Path.nexthop = mpattr.(*bgp.PathAttributeMpReachNLRI).Nexthop
	}
	return ipv6Path
}

func (ipv6p *IPv6Path) setPathDefault(pd *PathDefault) {
	ipv6p.PathDefault = pd
}

func (ipv6p *IPv6Path) getPathDefault() *PathDefault {
	return ipv6p.PathDefault
}

func (ipv6p *IPv6Path) getPrefix() net.IP {
	addrPrefix := ipv6p.nlri.(*bgp.IPv6AddrPrefix)
	return addrPrefix.Prefix
}

// return IPv6Path's string representation
func (ipv6p *IPv6Path) String() string {
	str := fmt.Sprintf("IPv6Path Source: %d, ", ipv6p.getSourceVerNum())
	str = str + fmt.Sprintf(" NLRI: %s, ", ipv6p.getPrefix().String())
	str = str + fmt.Sprintf(" nexthop: %s, ", ipv6p.getNexthop().String())
	str = str + fmt.Sprintf(" withdraw: %s, ", ipv6p.IsWithdraw())
	//str = str + fmt.Sprintf(" path attributes: %s, ", ipv6p.getPathAttributeMap())
	return str
}
<commit_msg>table: update path struct JSON format<commit_after>// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package table

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/osrg/gobgp/packet"
	"net"
	"reflect"
)

type Path interface {
	String() string
	GetPathAttrs() []bgp.PathAttributeInterface
	GetPathAttr(bgp.BGPAttrType) (int, bgp.PathAttributeInterface)
	getRouteFamily() RouteFamily
	setSource(source *PeerInfo)
	getSource() *PeerInfo
	setNexthop(nexthop net.IP)
	getNexthop() net.IP
	setSourceVerNum(sourceVerNum int)
	getSourceVerNum() int
	setWithdraw(withdraw bool)
	IsWithdraw() bool
	GetNlri() bgp.AddrPrefixInterface
	getPrefix() net.IP
	setMedSetByTargetNeighbor(medSetByTargetNeighbor bool)
	getMedSetByTargetNeighbor() bool
	Clone(IsWithdraw bool) Path
	setBest(isBest bool)
	MarshalJSON() ([]byte, error)
}

type PathDefault struct {
	routeFamily            RouteFamily
	source                 *PeerInfo
	nexthop                net.IP
	sourceVerNum           int
	withdraw               bool
	nlri                   bgp.AddrPrefixInterface
	pathAttrs              []bgp.PathAttributeInterface
	medSetByTargetNeighbor bool
	isBest                 bool
}

func NewPathDefault(rf RouteFamily, source *PeerInfo, nlri bgp.AddrPrefixInterface, sourceVerNum int, nexthop net.IP, isWithdraw bool, pattrs []bgp.PathAttributeInterface, medSetByTargetNeighbor bool) *PathDefault {

	if !isWithdraw && pattrs == nil {
		log.Error("Need to provide nexthop and patattrs for path that is not a withdraw.")
		return nil
	}

	path := &PathDefault{}
	path.routeFamily = rf
	path.pathAttrs = pattrs
	path.nlri = nlri
	path.source = source
	path.nexthop = nexthop
	path.sourceVerNum = sourceVerNum
	path.withdraw = isWithdraw
	path.medSetByTargetNeighbor = medSetByTargetNeighbor
	path.isBest = false
	return path
}

func (pd *PathDefault) setBest(isBest bool) {
	pd.isBest = isBest
}

func (pd *PathDefault) MarshalJSON() ([]byte, error) {
	// med := uint32(0)
	// _, attr := pd.GetPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
	// if attr != nil {
	// 	med = attr.(*bgp.PathAttributeMultiExitDisc).Value
	// }

	var prefix net.IP
	var prefixLen uint8
	switch nlri := pd.nlri.(type) {
	case *bgp.NLRInfo:
		prefix = nlri.Prefix
		prefixLen = nlri.Length
	}

	return json.Marshal(struct {
		Network string
		//Nexthop string
		Attrs   []bgp.PathAttributeInterface
		//Metric  string
		//origin string
		Best string
	}{
		Network: prefix.String() + "/" + fmt.Sprint(prefixLen),
		//Nexthop: pd.nexthop.String(),
		//Metric:  fmt.Sprint(med),
		Attrs:   pd.GetPathAttrs(),
		Best:    fmt.Sprint(pd.isBest),
	})
}

// create new PathAttributes
func (pd *PathDefault) Clone(isWithdraw bool) Path {
	copiedAttrs := []bgp.PathAttributeInterface(nil)
	nlri := pd.nlri
	if isWithdraw {
		if !pd.IsWithdraw() {
			nlri = &bgp.WithdrawnRoute{pd.nlri.(*bgp.NLRInfo).IPAddrPrefix}
		}
	} else {
		copiedAttrs = append(copiedAttrs, pd.pathAttrs...)
		for i, attr := range copiedAttrs {
			t, v := reflect.TypeOf(attr), reflect.ValueOf(attr)
			newAttrObjp := reflect.New(t.Elem())
			newAttrObjp.Elem().Set(v.Elem())
			copiedAttrs[i] = newAttrObjp.Interface().(bgp.PathAttributeInterface)
		}
	}
	return CreatePath(pd.source, nlri, copiedAttrs, isWithdraw)
}

func (pd *PathDefault) getRouteFamily() RouteFamily {
	return pd.routeFamily
}

func (pd *PathDefault) setSource(source *PeerInfo) {
	pd.source = source
}
func (pd *PathDefault) getSource() *PeerInfo {
	return pd.source
}

func (pd *PathDefault) setNexthop(nexthop net.IP) {
	pd.nexthop = nexthop
}

func (pd *PathDefault) getNexthop() net.IP {
	return pd.nexthop
}

func (pd *PathDefault) setSourceVerNum(sourceVerNum int) {
	pd.sourceVerNum = sourceVerNum
}

func (pd *PathDefault) getSourceVerNum() int {
	return pd.sourceVerNum
}

func (pd *PathDefault) setWithdraw(withdraw bool) {
	pd.withdraw = withdraw
}

func (pd *PathDefault) IsWithdraw() bool {
	return pd.withdraw
}

func (pd *PathDefault) GetNlri() bgp.AddrPrefixInterface {
	return pd.nlri
}

func (pd *PathDefault) setMedSetByTargetNeighbor(medSetByTargetNeighbor bool) {
	pd.medSetByTargetNeighbor = medSetByTargetNeighbor
}

func (pd *PathDefault) getMedSetByTargetNeighbor() bool {
	return pd.medSetByTargetNeighbor
}

func (pd *PathDefault) GetPathAttrs() []bgp.PathAttributeInterface {
	return pd.pathAttrs
}

func (pd *PathDefault) GetPathAttr(pattrType bgp.BGPAttrType) (int, bgp.PathAttributeInterface) {
	attrMap := [bgp.BGP_ATTR_TYPE_AS4_AGGREGATOR + 1]reflect.Type{}
	attrMap[bgp.BGP_ATTR_TYPE_ORIGIN] = reflect.TypeOf(&bgp.PathAttributeOrigin{})
	attrMap[bgp.BGP_ATTR_TYPE_AS_PATH] = reflect.TypeOf(&bgp.PathAttributeAsPath{})
	attrMap[bgp.BGP_ATTR_TYPE_NEXT_HOP] = reflect.TypeOf(&bgp.PathAttributeNextHop{})
	attrMap[bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC] = reflect.TypeOf(&bgp.PathAttributeMultiExitDisc{})
	attrMap[bgp.BGP_ATTR_TYPE_LOCAL_PREF] = reflect.TypeOf(&bgp.PathAttributeLocalPref{})
	attrMap[bgp.BGP_ATTR_TYPE_ATOMIC_AGGREGATE] = reflect.TypeOf(&bgp.PathAttributeAtomicAggregate{})
	attrMap[bgp.BGP_ATTR_TYPE_AGGREGATOR] = reflect.TypeOf(&bgp.PathAttributeAggregator{})
	attrMap[bgp.BGP_ATTR_TYPE_COMMUNITIES] = reflect.TypeOf(&bgp.PathAttributeCommunities{})
	attrMap[bgp.BGP_ATTR_TYPE_ORIGINATOR_ID] = reflect.TypeOf(&bgp.PathAttributeOriginatorId{})
	attrMap[bgp.BGP_ATTR_TYPE_CLUSTER_LIST] = reflect.TypeOf(&bgp.PathAttributeClusterList{})
	attrMap[bgp.BGP_ATTR_TYPE_MP_REACH_NLRI] = reflect.TypeOf(&bgp.PathAttributeMpReachNLRI{})
	attrMap[bgp.BGP_ATTR_TYPE_MP_UNREACH_NLRI] = reflect.TypeOf(&bgp.PathAttributeMpUnreachNLRI{})
	attrMap[bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES] = reflect.TypeOf(&bgp.PathAttributeExtendedCommunities{})
	attrMap[bgp.BGP_ATTR_TYPE_AS4_PATH] = reflect.TypeOf(&bgp.PathAttributeAs4Path{})
	attrMap[bgp.BGP_ATTR_TYPE_AS4_AGGREGATOR] = reflect.TypeOf(&bgp.PathAttributeAs4Aggregator{})

	t := attrMap[pattrType]
	for i, p := range pd.pathAttrs {
		if t == reflect.TypeOf(p) {
			return i, p
		}
	}
	return -1, nil
}

// return Path's string representation
func (pi *PathDefault) String() string {
	str := fmt.Sprintf("IPv4Path Source: %d, ", pi.getSourceVerNum())
	str = str + fmt.Sprintf(" NLRI: %s, ", pi.getPrefix().String())
	str = str + fmt.Sprintf(" nexthop: %s, ", pi.getNexthop().String())
	str = str + fmt.Sprintf(" withdraw: %s, ", pi.IsWithdraw())
	//str = str + fmt.Sprintf(" path attributes: %s, ", pi.getPathAttributeMap())
	return str
}

func (pi *PathDefault) getPrefix() net.IP {

	switch nlri := pi.nlri.(type) {
	case *bgp.NLRInfo:
		return nlri.Prefix
	case *bgp.WithdrawnRoute:
		return nlri.Prefix
	}
	return nil
}

// create Path object based on route family
func CreatePath(source *PeerInfo, nlri bgp.AddrPrefixInterface, attrs []bgp.PathAttributeInterface, isWithdraw bool) Path {

	rf := RouteFamily(int(nlri.AFI())<<16 | int(nlri.SAFI()))
	log.Debugf("afi: %d, safi: %d ", int(nlri.AFI()), nlri.SAFI())
	var path Path
	var sourceVerNum int = 1

	if source != nil {
		sourceVerNum = source.VersionNum
	}

	switch rf {
	case RF_IPv4_UC:
		log.Debugf("RouteFamily : %s", RF_IPv4_UC.String())
		path = NewIPv4Path(source, nlri, sourceVerNum, isWithdraw, attrs, false)
	case RF_IPv6_UC:
		log.Debugf("RouteFamily : %s", RF_IPv6_UC.String())
		path = NewIPv6Path(source, nlri, sourceVerNum, isWithdraw, attrs, false)
	}
	return path
}

/*
* 	Definition of inherited Path  interface
 */
type IPv4Path struct {
	*PathDefault
}

func NewIPv4Path(source *PeerInfo, nlri bgp.AddrPrefixInterface, sourceVerNum int, isWithdraw bool, attrs []bgp.PathAttributeInterface, medSetByTargetNeighbor bool) *IPv4Path {
	ipv4Path := &IPv4Path{}
	ipv4Path.PathDefault = NewPathDefault(RF_IPv4_UC, source, nlri, sourceVerNum, nil, isWithdraw, attrs, medSetByTargetNeighbor)
	if !isWithdraw {
		_, nexthop_attr := ipv4Path.GetPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
		ipv4Path.nexthop = nexthop_attr.(*bgp.PathAttributeNextHop).Value
	}
	return ipv4Path
}

func (ipv4p *IPv4Path) setPathDefault(pd *PathDefault) {
	ipv4p.PathDefault = pd
}
func (ipv4p *IPv4Path) getPathDefault() *PathDefault {
	return ipv4p.PathDefault
}

type IPv6Path struct {
	*PathDefault
}

func NewIPv6Path(source *PeerInfo, nlri bgp.AddrPrefixInterface, sourceVerNum int, isWithdraw bool, attrs []bgp.PathAttributeInterface, medSetByTargetNeighbor bool) *IPv6Path {
	ipv6Path := &IPv6Path{}
	ipv6Path.PathDefault = NewPathDefault(RF_IPv6_UC, source, nlri, sourceVerNum, nil, isWithdraw, attrs, medSetByTargetNeighbor)
	if !isWithdraw {
		_, mpattr := ipv6Path.GetPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
		ipv6Path.nexthop = mpattr.(*bgp.PathAttributeMpReachNLRI).Nexthop
	}
	return ipv6Path
}

func (ipv6p *IPv6Path) setPathDefault(pd *PathDefault) {
	ipv6p.PathDefault = pd
}

func (ipv6p *IPv6Path) getPathDefault() *PathDefault {
	return ipv6p.PathDefault
}

func (ipv6p *IPv6Path) getPrefix() net.IP {
	addrPrefix := ipv6p.nlri.(*bgp.IPv6AddrPrefix)
	return addrPrefix.Prefix
}

// return IPv6Path's string representation
func (ipv6p *IPv6Path) String() string {
	str := fmt.Sprintf("IPv6Path Source: %d, ", ipv6p.getSourceVerNum())
	str = str + fmt.Sprintf(" NLRI: %s, ", ipv6p.getPrefix().String())
	str = str + fmt.Sprintf(" nexthop: %s, ", ipv6p.getNexthop().String())
	str = str + fmt.Sprintf(" withdraw: %s, ", ipv6p.IsWithdraw())
	//str = str + fmt.Sprintf(" path attributes: %s, ", ipv6p.getPathAttributeMap())
	return str
}
<|endoftext|>


<commit_before>// Copyright 2014 Oleku Konko All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// This module is a Table Writer  API for the Go Programming Language.
// The protocols were written in pure Go and works on windows and unix systems

package tablewriter

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
	"testing"
)

func ExampleShort() {

	data := [][]string{
		[]string{"A", "The Good", "500"},
		[]string{"B", "The Very very Bad Man", "288"},
		[]string{"C", "The Ugly", "120"},
		[]string{"D", "The Gopher", "800"},
	}

	table := NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "Sign", "Rating"})

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

}

func ExampleLong() {

	data := [][]string{
		[]string{"Learn East has computers with adapted keyboards with enlarged print etc", "  Some Data  ", " Another Data"},
		[]string{"Instead of lining up the letters all ", "the way across, he splits the keyboard in two", "Like most ergonomic keyboards", "See Data"},
	}

	table := NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "Sign", "Rating"})
	table.SetCenterSeparator("*")
	table.SetRowSeparator("=")

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

}

func ExampleCSV() {
	table, _ := NewCSV(os.Stdout, "test.csv", true)
	table.SetCenterSeparator("*")
	table.SetRowSeparator("=")

	table.Render()
}

func TestCSVInfo(t *testing.T) {
	table, err := NewCSV(os.Stdout, "test_info.csv", true)
	if err != nil {
		t.Error(err)
		return
	}
	table.SetAlignment(ALIGN_LEFT)
	table.SetBorder(false)
	table.Render()
}

func TestCSVSeparator(t *testing.T) {
	table, err := NewCSV(os.Stdout, "test.csv", true)
	if err != nil {
		t.Error(err)
		return
	}
	table.SetRowLine(true)
	table.SetCenterSeparator("*")
	table.SetColumnSeparator("‡")
	table.SetRowSeparator("-")
	table.SetAlignment(ALIGN_LEFT)
	table.Render()
}

func TestBorder(t *testing.T) {
	data := [][]string{
		[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
		[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
		[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
		[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
	}

	table := NewWriter(os.Stdout)
	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
	table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
	table.SetBorder(false)                                // Set Border to false
	table.AppendBulk(data)                                // Add Bulk Data
	table.Render()
}

func TestPrintHeading(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.printHeading()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	got := buf.String()
	if got != want {
		t.Errorf("header rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintHeadingWithoutAutoFormat(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.SetAutoFormatHeaders(false)
	table.printHeading()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	got := buf.String()
	if got != want {
		t.Errorf("header rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintFooter(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.printFooter()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	got := buf.String()
	if got != want {
		t.Errorf("footer rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintFooterWithoutAutoFormat(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetAutoFormatHeaders(false)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.printFooter()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	got := buf.String()
	if got != want {
		t.Errorf("footer rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintTableWithAndWithoutAutoWrap(t *testing.T) {
	var buf bytes.Buffer
	var multiline = `A multiline
string with some lines being really long.`

	with := NewWriter(&buf)
	with.Append([]string{multiline})
	with.Render()
	want := `+--------------------------------+
| A multiline string with some   |
| lines being really long.       |
+--------------------------------+
`
	got := buf.String()
	if got != want {
		t.Errorf("multiline text rendering with wrapping failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}

	buf.Truncate(0)
	without := NewWriter(&buf)
	without.SetAutoWrapText(false)
	without.Append([]string{multiline})
	without.Render()
	want = `+-------------------------------------------+
| A multiline                               |
| string with some lines being really long. |
+-------------------------------------------+
`
	got = buf.String()
	if got != want {
		t.Errorf("multiline text rendering without wrapping rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintLine(t *testing.T) {
	header := make([]string, 12)
	val := " "
	want := ""
	for i := range header {
		header[i] = val
		want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1))
		val = val + " "
	}
	want = want + "+"
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader(header)
	table.printLine(false)
	got := buf.String()
	if got != want {
		t.Errorf("line rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestAnsiStrip(t *testing.T) {
	header := make([]string, 12)
	val := " "
	want := ""
	for i := range header {
		header[i] = "\033[43;30m" + val + "\033[00m"
		want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1))
		val = val + " "
	}
	want = want + "+"
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader(header)
	table.printLine(false)
	got := buf.String()
	if got != want {
		t.Errorf("line rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func NewCustomizedTable(out io.Writer) *Table {
	table := NewWriter(out)
	table.SetCenterSeparator("")
	table.SetColumnSeparator("")
	table.SetRowSeparator("")
	table.SetBorder(false)
	table.SetAlignment(ALIGN_LEFT)
	table.SetHeader([]string{})
	return table
}

func TestSubclass(t *testing.T) {
	buf := new(bytes.Buffer)
	table := NewCustomizedTable(buf)

	data := [][]string{
		[]string{"A", "The Good", "500"},
		[]string{"B", "The Very very Bad Man", "288"},
		[]string{"C", "The Ugly", "120"},
		[]string{"D", "The Gopher", "800"},
	}

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

	output := string(buf.Bytes())
	want := `  A  The Good               500
  B  The Very very Bad Man  288
  C  The Ugly               120
  D  The Gopher             800
`
	if output != want {
		t.Error(fmt.Sprintf("Unexpected output '%v' != '%v'", output, want))
	}
}
<commit_msg>tests: border table<commit_after>// Copyright 2014 Oleku Konko All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// This module is a Table Writer  API for the Go Programming Language.
// The protocols were written in pure Go and works on windows and unix systems

package tablewriter

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
	"testing"
)

func ExampleShort() {

	data := [][]string{
		[]string{"A", "The Good", "500"},
		[]string{"B", "The Very very Bad Man", "288"},
		[]string{"C", "The Ugly", "120"},
		[]string{"D", "The Gopher", "800"},
	}

	table := NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "Sign", "Rating"})

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

}

func ExampleLong() {

	data := [][]string{
		[]string{"Learn East has computers with adapted keyboards with enlarged print etc", "  Some Data  ", " Another Data"},
		[]string{"Instead of lining up the letters all ", "the way across, he splits the keyboard in two", "Like most ergonomic keyboards", "See Data"},
	}

	table := NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "Sign", "Rating"})
	table.SetCenterSeparator("*")
	table.SetRowSeparator("=")

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

}

func ExampleCSV() {
	table, _ := NewCSV(os.Stdout, "test.csv", true)
	table.SetCenterSeparator("*")
	table.SetRowSeparator("=")

	table.Render()
}

func TestCSVInfo(t *testing.T) {
	table, err := NewCSV(os.Stdout, "test_info.csv", true)
	if err != nil {
		t.Error(err)
		return
	}
	table.SetAlignment(ALIGN_LEFT)
	table.SetBorder(false)
	table.Render()
}

func TestCSVSeparator(t *testing.T) {
	table, err := NewCSV(os.Stdout, "test.csv", true)
	if err != nil {
		t.Error(err)
		return
	}
	table.SetRowLine(true)
	table.SetCenterSeparator("*")
	table.SetColumnSeparator("‡")
	table.SetRowSeparator("-")
	table.SetAlignment(ALIGN_LEFT)
	table.Render()
}

func TestBorder(t *testing.T) {
	data := [][]string{
		[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
		[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
		[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
		[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
	}

	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
	table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
	table.SetBorder(false)                                // Set Border to false
	table.AppendBulk(data)                                // Add Bulk Data
	table.Render()

	want := `    DATE   |       DESCRIPTION        |  CV2  | AMOUNT
+----------+--------------------------+-------+---------+
  1/1/2014 | Domain name              |  2233 | $10.98
  1/1/2014 | January Hosting          |  2233 | $54.95
  1/4/2014 | February Hosting         |  2233 | $51.00
  1/4/2014 | February Extra Bandwidth |  2233 | $30.00
+----------+--------------------------+-------+---------+
                                        TOTAL | $146 93
                                      +-------+---------+
`
	got := buf.String()
	if got != want {
		t.Errorf("border table rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintHeading(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.printHeading()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	got := buf.String()
	if got != want {
		t.Errorf("header rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintHeadingWithoutAutoFormat(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.SetAutoFormatHeaders(false)
	table.printHeading()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	got := buf.String()
	if got != want {
		t.Errorf("header rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintFooter(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.printFooter()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	got := buf.String()
	if got != want {
		t.Errorf("footer rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintFooterWithoutAutoFormat(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetAutoFormatHeaders(false)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.printFooter()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	got := buf.String()
	if got != want {
		t.Errorf("footer rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintTableWithAndWithoutAutoWrap(t *testing.T) {
	var buf bytes.Buffer
	var multiline = `A multiline
string with some lines being really long.`

	with := NewWriter(&buf)
	with.Append([]string{multiline})
	with.Render()
	want := `+--------------------------------+
| A multiline string with some   |
| lines being really long.       |
+--------------------------------+
`
	got := buf.String()
	if got != want {
		t.Errorf("multiline text rendering with wrapping failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}

	buf.Truncate(0)
	without := NewWriter(&buf)
	without.SetAutoWrapText(false)
	without.Append([]string{multiline})
	without.Render()
	want = `+-------------------------------------------+
| A multiline                               |
| string with some lines being really long. |
+-------------------------------------------+
`
	got = buf.String()
	if got != want {
		t.Errorf("multiline text rendering without wrapping rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestPrintLine(t *testing.T) {
	header := make([]string, 12)
	val := " "
	want := ""
	for i := range header {
		header[i] = val
		want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1))
		val = val + " "
	}
	want = want + "+"
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader(header)
	table.printLine(false)
	got := buf.String()
	if got != want {
		t.Errorf("line rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func TestAnsiStrip(t *testing.T) {
	header := make([]string, 12)
	val := " "
	want := ""
	for i := range header {
		header[i] = "\033[43;30m" + val + "\033[00m"
		want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1))
		val = val + " "
	}
	want = want + "+"
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader(header)
	table.printLine(false)
	got := buf.String()
	if got != want {
		t.Errorf("line rendering failed\ngot:\n%s\nwant:\n%s\n", got, want)
	}
}

func NewCustomizedTable(out io.Writer) *Table {
	table := NewWriter(out)
	table.SetCenterSeparator("")
	table.SetColumnSeparator("")
	table.SetRowSeparator("")
	table.SetBorder(false)
	table.SetAlignment(ALIGN_LEFT)
	table.SetHeader([]string{})
	return table
}

func TestSubclass(t *testing.T) {
	buf := new(bytes.Buffer)
	table := NewCustomizedTable(buf)

	data := [][]string{
		[]string{"A", "The Good", "500"},
		[]string{"B", "The Very very Bad Man", "288"},
		[]string{"C", "The Ugly", "120"},
		[]string{"D", "The Gopher", "800"},
	}

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

	output := string(buf.Bytes())
	want := `  A  The Good               500
  B  The Very very Bad Man  288
  C  The Ugly               120
  D  The Gopher             800
`
	if output != want {
		t.Error(fmt.Sprintf("Unexpected output '%v' != '%v'", output, want))
	}
}
<|endoftext|>


<commit_before>package pqt_test

import (
	"testing"

	"github.com/piotrkowalczuk/pqt"
)

func TestNewTable(t *testing.T) {
	tbl := pqt.NewTable("test", pqt.WithIfNotExists(), pqt.WithTableSpace("table_space"), pqt.WithTemporary())

	if !tbl.IfNotExists {
		t.Errorf("table should have field if not exists set to true")
	}

	if !tbl.Temporary {
		t.Errorf("table should have field temporary set to true")
	}

	if tbl.TableSpace != "table_space" {
		t.Errorf("table should have field table space set to table_space")
	}
}

func TestTable_AddColumn(t *testing.T) {
	c1 := &pqt.Column{Name: "c1"}
	c2 := &pqt.Column{Name: "c2"}
	c3 := &pqt.Column{Name: "c3"}

	tbl := pqt.NewTable("test").
		AddColumn(c1).
		AddColumn(c2).
		AddColumn(c3)

	if len(tbl.Columns) != 3 {
		t.Errorf("wrong number of colums, expected %d but got %d", 3, len(tbl.Columns))
	}

	for i, c := range tbl.Columns {
		if c.Name == "" {
			t.Errorf("column #%d table name is empty", i)
		}
		if c.Table == nil {
			t.Errorf("column #%d table nil pointer", i)
		}
	}
}

func TestTable_AddRelationship_oneToOneBidirectional(t *testing.T) {
	user := pqt.NewTable("user").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))
	userDetail := pqt.NewTable("user_detail").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))

	user.AddRelationship(pqt.OneToOne(
		userDetail,
		pqt.WithInversedName("details"),
		pqt.WithOwnerName("user"),
		pqt.WithBidirectional(),
	))

	if len(user.OwnedRelationships) != 1 {
		t.Fatalf("user should have 1 relationship, but has %d", len(user.OwnedRelationships))
	}

	if user.OwnedRelationships[0].OwnerName != "user" {
		t.Errorf("user relationship to user_detail should be mapped by user, but is %s", user.OwnedRelationships[0].OwnerName)
	}

	if user.OwnedRelationships[0].OwnerTable != user {
		t.Errorf("user relationship to user_detail should be mapped by user table, but is %s", user.OwnedRelationships[0].OwnerTable)
	}

	if user.OwnedRelationships[0].Type != pqt.RelationshipTypeOneToOne {
		t.Errorf("user relationship to user_detail should be one to one bidirectional")
	}

	if len(userDetail.InversedRelationships) != 1 {
		t.Fatalf("user_detail should have 1 relationship, but has %d", len(userDetail.InversedRelationships))
	}

	if userDetail.InversedRelationships[0].InversedName != "details" {
		t.Errorf("user_detail relationship to user should be mapped by user")
	}

	if userDetail.InversedRelationships[0].InversedTable != userDetail {
		t.Errorf("user_detail relationship to user should be mapped by user_detail table")
	}

	if userDetail.InversedRelationships[0].Type != pqt.RelationshipTypeOneToOne {
		t.Errorf("user_detail relationship to user should be %d, but is %d", pqt.RelationshipTypeOneToOne, userDetail.InversedRelationships[0].Type)
	}
}

func TestTable_AddRelationship_oneToOneUnidirectional(t *testing.T) {
	user := pqt.NewTable("user").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))
	userDetail := pqt.NewTable("user_detail").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey())).
		AddRelationship(pqt.OneToOne(
			user,
			pqt.WithInversedName("user"),
			pqt.WithOwnerName("details"),
		))

	if len(user.InversedRelationships) != 0 {
		t.Fatalf("user should have 0 relationship, but has %d", len(user.InversedRelationships))
	}

	if len(userDetail.OwnedRelationships) != 1 {
		t.Fatalf("user_detail should have 1 relationship, but has %d", len(userDetail.OwnedRelationships))
	}

	if userDetail.OwnedRelationships[0].InversedName != "user" {
		t.Errorf("user_detail relationship to user should be mapped by user")
	}

	if userDetail.OwnedRelationships[0].InversedTable != user {
		t.Errorf("user_detail relationship to user should be mapped by user table")
	}

	if userDetail.OwnedRelationships[0].Type != pqt.RelationshipTypeOneToOne {
		t.Errorf("user_detail relationship to user should be %d, but is %d", pqt.RelationshipTypeOneToOne, userDetail.OwnedRelationships[0].Type)
	}
}

func TestTable_AddRelationship_oneToOneSelfReferencing(t *testing.T) {
	user := pqt.NewTable("user").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))

	user.AddRelationship(pqt.OneToOne(
		pqt.SelfReference(),
		pqt.WithInversedName("child"),
		pqt.WithOwnerName("parent"),
	))

	if len(user.OwnedRelationships) != 1 {
		t.Fatalf("user should have 1 owned relationship, but has %d", len(user.OwnedRelationships))
	}

	if user.OwnedRelationships[0].OwnerName != "parent" {
		t.Errorf("user relationship to user should be mapped by parent")
	}

	if user.OwnedRelationships[0].OwnerTable != user {
		t.Errorf("user relationship to user should be mapped by user table")
	}

	if user.OwnedRelationships[0].Type != pqt.RelationshipTypeOneToOne {
		t.Errorf("user relationship to user should be %d, but is %d", pqt.RelationshipTypeOneToOne, user.OwnedRelationships[0].Type)
	}

	if len(user.InversedRelationships) != 0 {
		t.Fatalf("user should have 0 inversed relationship, but has %d", len(user.InversedRelationships))
	}
}

func TestTable_AddRelationship_oneToMany(t *testing.T) {
	user := pqt.NewTable("user").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))
	comment := pqt.NewTable("comment").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))

	user.AddRelationship(pqt.OneToMany(
		comment,
		pqt.WithBidirectional(),
		pqt.WithInversedName("author"),
		pqt.WithOwnerName("comments"),
	))

	if len(user.InversedRelationships) != 1 {
		t.Fatalf("user should have 1 inversed relationship, but has %d", len(user.InversedRelationships))
	}

	if user.InversedRelationships[0].OwnerName != "comments" {
		t.Errorf("user inversed relationship to comment should be mapped by comments")
	}

	if user.InversedRelationships[0].OwnerTable != comment {
		t.Errorf("user inversed relationship to comment should be mapped by comment table")
	}

	if user.InversedRelationships[0].Type != pqt.RelationshipTypeOneToMany {
		t.Errorf("user inversed relationship to comment should be one to many")
	}

	if len(comment.OwnedRelationships) != 1 {
		t.Fatalf("comment should have 1 owned relationship, but has %d", len(comment.OwnedRelationships))
	}

	if comment.OwnedRelationships[0].InversedName != "author" {
		t.Errorf("comment relationship to user should be mapped by author")
	}

	if comment.OwnedRelationships[0].InversedTable != user {
		t.Errorf("comment relationship to user should be mapped by user table")
	}

	if comment.OwnedRelationships[0].Type != pqt.RelationshipTypeOneToMany {
		t.Errorf("comment relationship to user should be %d, but is %d", pqt.RelationshipTypeOneToMany, comment.OwnedRelationships[0].Type)
	}
}

//func TestTable_AddRelationship_manyToMany(t *testing.T) {
//	user := pqt.NewTable("user").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))
//	group := pqt.NewTable("group").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))
//	userGroups := pqt.NewTable("user_groups")
//	user.AddRelationship(pqt.ManyToMany(
//		group,
//		userGroups,
//		pqt.WithInversedName("users"),
//		pqt.WithOwnerName("groups"),
//	))
//
//	if len(user.Relationships) != 1 {
//		t.Fatalf("user should have 1 relationship, but has %d", len(user.Relationships))
//	}
//
//	if user.Relationships[0].OwnerName != "groups" {
//		t.Errorf("user relationship to group should be mapped by groups")
//	}
//
//	if user.Relationships[0].OwnerTable != group {
//		t.Errorf("user relationship to group should be mapped by group table")
//	}
//
//	if user.Relationships[0].Type != pqt.RelationshipTypeManyToMany {
//		t.Errorf("user relationship to group should be many to many")
//	}
//
//	if len(group.Relationships) != 1 {
//		t.Fatalf("group should have 1 relationship, but has %d", len(group.Relationships))
//	}
//
//	if group.Relationships[0].InversedName != "users" {
//		t.Errorf("group relationship to user should be mapped by users")
//	}
//
//	if group.Relationships[0].InversedTable != user {
//		t.Errorf("group relationship to user should be mapped by user table")
//	}
//
//	if group.Relationships[0].Type != pqt.RelationshipTypeManyToMany {
//		t.Errorf("group relationship to user should be %d, but is %d", pqt.RelationshipTypeManyToMany, group.Relationships[0].Type)
//	}
//}
//
//func TestTable_AddRelationship_manyToManySelfReferencing(t *testing.T) {
//	friendship := pqt.NewTable("friendship")
//	user := pqt.NewTable("user").
//		AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey())).
//		AddRelationship(pqt.ManyToManySelfReferencing(
//		friendship,
//		pqt.WithInversedName("friends_with_me"),
//		pqt.WithOwnerName("my_friends"),
//	))
//
//	if len(user.Relationships) != 2 {
//		t.Fatalf("user should have 2 relationships, but has %d", len(user.Relationships))
//	}
//
//	if user.Relationships[0].OwnerName != "my_friends" {
//		t.Errorf("user relationship to user should be mapped by my_friends")
//	}
//
//	if user.Relationships[0].OwnerTable != user {
//		t.Errorf("user relationship to group should be mapped by group table")
//	}
//
//	if user.Relationships[0].Type != pqt.RelationshipTypeManyToManySelfReferencing {
//		t.Errorf("user relationship to group should be many to many")
//	}
//
//	if user.Relationships[1].InversedName != "friends_with_me" {
//		t.Errorf("user relationship to user should be mapped by friends_with_me")
//	}
//
//	if user.Relationships[1].InversedTable != user {
//		t.Errorf("user relationship to user should be mapped by user table")
//	}
//
//	if user.Relationships[1].Type != pqt.RelationshipTypeManyToManySelfReferencing {
//		t.Errorf("user relationship to user should be %d, but is %d", pqt.RelationshipTypeManyToManySelfReferencing, user.Relationships[1].Type)
//	}
//}
<commit_msg>improved TestTable_AddColumn so it cover more<commit_after>package pqt_test

import (
	"testing"

	"github.com/piotrkowalczuk/pqt"
)

func TestNewTable(t *testing.T) {
	tbl := pqt.NewTable("test", pqt.WithIfNotExists(), pqt.WithTableSpace("table_space"), pqt.WithTemporary())

	if !tbl.IfNotExists {
		t.Errorf("table should have field if not exists set to true")
	}

	if !tbl.Temporary {
		t.Errorf("table should have field temporary set to true")
	}

	if tbl.TableSpace != "table_space" {
		t.Errorf("table should have field table space set to table_space")
	}
}

func TestTable_AddColumn(t *testing.T) {
	c1 := &pqt.Column{Name: "c1"}
	c2 := &pqt.Column{Name: "c2"}
	c3 := &pqt.Column{Name: "c3"}

	tbl := pqt.NewTable("test").
		AddColumn(pqt.NewColumn("c0", pqt.TypeSerialBig(), pqt.WithPrimaryKey())).
		AddColumn(c1).
		AddColumn(c2).
		AddColumn(c3).
		AddRelationship(pqt.ManyToOne(pqt.SelfReference()))

	if len(tbl.Columns) != 5 {
		t.Errorf("wrong number of colums, expected %d but got %d", 5, len(tbl.Columns))
	}

	if len(tbl.OwnedRelationships) != 1 {
		t.Errorf("wrong number of owned relationships, expected %d but got %d", 1, len(tbl.OwnedRelationships))
	}

	for i, c := range tbl.Columns {
		if c.Name == "" {
			t.Errorf("column #%d table name is empty", i)
		}
		if c.Table == nil {
			t.Errorf("column #%d table nil pointer", i)
		}
	}
}

func TestTable_AddRelationship_oneToOneBidirectional(t *testing.T) {
	user := pqt.NewTable("user").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))
	userDetail := pqt.NewTable("user_detail").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))

	user.AddRelationship(pqt.OneToOne(
		userDetail,
		pqt.WithInversedName("details"),
		pqt.WithOwnerName("user"),
		pqt.WithBidirectional(),
	))

	if len(user.OwnedRelationships) != 1 {
		t.Fatalf("user should have 1 relationship, but has %d", len(user.OwnedRelationships))
	}

	if user.OwnedRelationships[0].OwnerName != "user" {
		t.Errorf("user relationship to user_detail should be mapped by user, but is %s", user.OwnedRelationships[0].OwnerName)
	}

	if user.OwnedRelationships[0].OwnerTable != user {
		t.Errorf("user relationship to user_detail should be mapped by user table, but is %s", user.OwnedRelationships[0].OwnerTable)
	}

	if user.OwnedRelationships[0].Type != pqt.RelationshipTypeOneToOne {
		t.Errorf("user relationship to user_detail should be one to one bidirectional")
	}

	if len(userDetail.InversedRelationships) != 1 {
		t.Fatalf("user_detail should have 1 relationship, but has %d", len(userDetail.InversedRelationships))
	}

	if userDetail.InversedRelationships[0].InversedName != "details" {
		t.Errorf("user_detail relationship to user should be mapped by user")
	}

	if userDetail.InversedRelationships[0].InversedTable != userDetail {
		t.Errorf("user_detail relationship to user should be mapped by user_detail table")
	}

	if userDetail.InversedRelationships[0].Type != pqt.RelationshipTypeOneToOne {
		t.Errorf("user_detail relationship to user should be %d, but is %d", pqt.RelationshipTypeOneToOne, userDetail.InversedRelationships[0].Type)
	}
}

func TestTable_AddRelationship_oneToOneUnidirectional(t *testing.T) {
	user := pqt.NewTable("user").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))
	userDetail := pqt.NewTable("user_detail").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey())).
		AddRelationship(pqt.OneToOne(
			user,
			pqt.WithInversedName("user"),
			pqt.WithOwnerName("details"),
		))

	if len(user.InversedRelationships) != 0 {
		t.Fatalf("user should have 0 relationship, but has %d", len(user.InversedRelationships))
	}

	if len(userDetail.OwnedRelationships) != 1 {
		t.Fatalf("user_detail should have 1 relationship, but has %d", len(userDetail.OwnedRelationships))
	}

	if userDetail.OwnedRelationships[0].InversedName != "user" {
		t.Errorf("user_detail relationship to user should be mapped by user")
	}

	if userDetail.OwnedRelationships[0].InversedTable != user {
		t.Errorf("user_detail relationship to user should be mapped by user table")
	}

	if userDetail.OwnedRelationships[0].Type != pqt.RelationshipTypeOneToOne {
		t.Errorf("user_detail relationship to user should be %d, but is %d", pqt.RelationshipTypeOneToOne, userDetail.OwnedRelationships[0].Type)
	}
}

func TestTable_AddRelationship_oneToOneSelfReferencing(t *testing.T) {
	user := pqt.NewTable("user").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))

	user.AddRelationship(pqt.OneToOne(
		pqt.SelfReference(),
		pqt.WithInversedName("child"),
		pqt.WithOwnerName("parent"),
	))

	if len(user.OwnedRelationships) != 1 {
		t.Fatalf("user should have 1 owned relationship, but has %d", len(user.OwnedRelationships))
	}

	if user.OwnedRelationships[0].OwnerName != "parent" {
		t.Errorf("user relationship to user should be mapped by parent")
	}

	if user.OwnedRelationships[0].OwnerTable != user {
		t.Errorf("user relationship to user should be mapped by user table")
	}

	if user.OwnedRelationships[0].Type != pqt.RelationshipTypeOneToOne {
		t.Errorf("user relationship to user should be %d, but is %d", pqt.RelationshipTypeOneToOne, user.OwnedRelationships[0].Type)
	}

	if len(user.InversedRelationships) != 0 {
		t.Fatalf("user should have 0 inversed relationship, but has %d", len(user.InversedRelationships))
	}
}

func TestTable_AddRelationship_oneToMany(t *testing.T) {
	user := pqt.NewTable("user").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))
	comment := pqt.NewTable("comment").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))

	user.AddRelationship(pqt.OneToMany(
		comment,
		pqt.WithBidirectional(),
		pqt.WithInversedName("author"),
		pqt.WithOwnerName("comments"),
	))

	if len(user.InversedRelationships) != 1 {
		t.Fatalf("user should have 1 inversed relationship, but has %d", len(user.InversedRelationships))
	}

	if user.InversedRelationships[0].OwnerName != "comments" {
		t.Errorf("user inversed relationship to comment should be mapped by comments")
	}

	if user.InversedRelationships[0].OwnerTable != comment {
		t.Errorf("user inversed relationship to comment should be mapped by comment table")
	}

	if user.InversedRelationships[0].Type != pqt.RelationshipTypeOneToMany {
		t.Errorf("user inversed relationship to comment should be one to many")
	}

	if len(comment.OwnedRelationships) != 1 {
		t.Fatalf("comment should have 1 owned relationship, but has %d", len(comment.OwnedRelationships))
	}

	if comment.OwnedRelationships[0].InversedName != "author" {
		t.Errorf("comment relationship to user should be mapped by author")
	}

	if comment.OwnedRelationships[0].InversedTable != user {
		t.Errorf("comment relationship to user should be mapped by user table")
	}

	if comment.OwnedRelationships[0].Type != pqt.RelationshipTypeOneToMany {
		t.Errorf("comment relationship to user should be %d, but is %d", pqt.RelationshipTypeOneToMany, comment.OwnedRelationships[0].Type)
	}
}

//func TestTable_AddRelationship_manyToMany(t *testing.T) {
//	user := pqt.NewTable("user").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))
//	group := pqt.NewTable("group").AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey()))
//	userGroups := pqt.NewTable("user_groups")
//	user.AddRelationship(pqt.ManyToMany(
//		group,
//		userGroups,
//		pqt.WithInversedName("users"),
//		pqt.WithOwnerName("groups"),
//	))
//
//	if len(user.Relationships) != 1 {
//		t.Fatalf("user should have 1 relationship, but has %d", len(user.Relationships))
//	}
//
//	if user.Relationships[0].OwnerName != "groups" {
//		t.Errorf("user relationship to group should be mapped by groups")
//	}
//
//	if user.Relationships[0].OwnerTable != group {
//		t.Errorf("user relationship to group should be mapped by group table")
//	}
//
//	if user.Relationships[0].Type != pqt.RelationshipTypeManyToMany {
//		t.Errorf("user relationship to group should be many to many")
//	}
//
//	if len(group.Relationships) != 1 {
//		t.Fatalf("group should have 1 relationship, but has %d", len(group.Relationships))
//	}
//
//	if group.Relationships[0].InversedName != "users" {
//		t.Errorf("group relationship to user should be mapped by users")
//	}
//
//	if group.Relationships[0].InversedTable != user {
//		t.Errorf("group relationship to user should be mapped by user table")
//	}
//
//	if group.Relationships[0].Type != pqt.RelationshipTypeManyToMany {
//		t.Errorf("group relationship to user should be %d, but is %d", pqt.RelationshipTypeManyToMany, group.Relationships[0].Type)
//	}
//}
//
//func TestTable_AddRelationship_manyToManySelfReferencing(t *testing.T) {
//	friendship := pqt.NewTable("friendship")
//	user := pqt.NewTable("user").
//		AddColumn(pqt.NewColumn("id", pqt.TypeSerial(), pqt.WithPrimaryKey())).
//		AddRelationship(pqt.ManyToManySelfReferencing(
//		friendship,
//		pqt.WithInversedName("friends_with_me"),
//		pqt.WithOwnerName("my_friends"),
//	))
//
//	if len(user.Relationships) != 2 {
//		t.Fatalf("user should have 2 relationships, but has %d", len(user.Relationships))
//	}
//
//	if user.Relationships[0].OwnerName != "my_friends" {
//		t.Errorf("user relationship to user should be mapped by my_friends")
//	}
//
//	if user.Relationships[0].OwnerTable != user {
//		t.Errorf("user relationship to group should be mapped by group table")
//	}
//
//	if user.Relationships[0].Type != pqt.RelationshipTypeManyToManySelfReferencing {
//		t.Errorf("user relationship to group should be many to many")
//	}
//
//	if user.Relationships[1].InversedName != "friends_with_me" {
//		t.Errorf("user relationship to user should be mapped by friends_with_me")
//	}
//
//	if user.Relationships[1].InversedTable != user {
//		t.Errorf("user relationship to user should be mapped by user table")
//	}
//
//	if user.Relationships[1].Type != pqt.RelationshipTypeManyToManySelfReferencing {
//		t.Errorf("user relationship to user should be %d, but is %d", pqt.RelationshipTypeManyToManySelfReferencing, user.Relationships[1].Type)
//	}
//}
<|endoftext|>