package libpod

import (
	"testing"

	"github.com/containers/libpod/libpod/lock"
	"github.com/stretchr/testify/assert"
)

func TestBuildContainerGraphNoCtrsIsEmpty(t *testing.T) {
	graph, err := BuildContainerGraph([]*Container{})
	assert.NoError(t, err)
	assert.Equal(t, 0, len(graph.nodes))
	assert.Equal(t, 0, len(graph.noDepNodes))
	assert.Equal(t, 0, len(graph.notDependedOnNodes))
}

func TestBuildContainerGraphOneCtr(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)

	graph, err := BuildContainerGraph([]*Container{ctr1})
	assert.NoError(t, err)
	assert.Equal(t, 1, len(graph.nodes))
	assert.Equal(t, 1, len(graph.noDepNodes))
	assert.Equal(t, 1, len(graph.notDependedOnNodes))

	node, ok := graph.nodes[ctr1.ID()]
	assert.True(t, ok)
	assert.Equal(t, ctr1.ID(), node.id)

	assert.Equal(t, ctr1.ID(), graph.noDepNodes[0].id)
	assert.Equal(t, ctr1.ID(), graph.notDependedOnNodes[ctr1.ID()].id)
}

func TestBuildContainerGraphTwoCtrNoEdge(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)

	graph, err := BuildContainerGraph([]*Container{ctr1, ctr2})
	assert.NoError(t, err)
	assert.Equal(t, 2, len(graph.nodes))
	assert.Equal(t, 2, len(graph.noDepNodes))
	assert.Equal(t, 2, len(graph.notDependedOnNodes))

	node1, ok := graph.nodes[ctr1.ID()]
	assert.True(t, ok)
	assert.Equal(t, ctr1.ID(), node1.id)

	node2, ok := graph.nodes[ctr2.ID()]
	assert.True(t, ok)
	assert.Equal(t, ctr2.ID(), node2.id)
}

func TestBuildContainerGraphTwoCtrOneEdge(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)
	ctr2.config.UserNsCtr = ctr1.config.ID

	graph, err := BuildContainerGraph([]*Container{ctr1, ctr2})
	assert.NoError(t, err)
	assert.Equal(t, 2, len(graph.nodes))
	assert.Equal(t, 1, len(graph.noDepNodes))
	assert.Equal(t, 1, len(graph.notDependedOnNodes))

	assert.Equal(t, ctr1.ID(), graph.noDepNodes[0].id)
	assert.Equal(t, ctr2.ID(), graph.notDependedOnNodes[ctr2.ID()].id)
}

func TestBuildContainerGraphTwoCtrCycle(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)
	ctr2.config.UserNsCtr = ctr1.config.ID
	ctr1.config.NetNsCtr = ctr2.config.ID

	_, err = BuildContainerGraph([]*Container{ctr1, ctr2})
	assert.Error(t, err)
}

func TestBuildContainerGraphThreeCtrNoEdges(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)
	ctr3, err := getTestCtrN("3", manager)
	assert.NoError(t, err)

	graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
	assert.NoError(t, err)
	assert.Equal(t, 3, len(graph.nodes))
	assert.Equal(t, 3, len(graph.noDepNodes))
	assert.Equal(t, 3, len(graph.notDependedOnNodes))

	node1, ok := graph.nodes[ctr1.ID()]
	assert.True(t, ok)
	assert.Equal(t, ctr1.ID(), node1.id)

	node2, ok := graph.nodes[ctr2.ID()]
	assert.True(t, ok)
	assert.Equal(t, ctr2.ID(), node2.id)

	node3, ok := graph.nodes[ctr3.ID()]
	assert.True(t, ok)
	assert.Equal(t, ctr3.ID(), node3.id)
}

func TestBuildContainerGraphThreeContainersTwoInCycle(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)
	ctr3, err := getTestCtrN("3", manager)
	assert.NoError(t, err)
	ctr1.config.UserNsCtr = ctr2.config.ID
	ctr2.config.IPCNsCtr = ctr1.config.ID

	_, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
	assert.Error(t, err)
}

func TestBuildContainerGraphThreeContainersCycle(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)
	ctr3, err := getTestCtrN("3", manager)
	assert.NoError(t, err)
	ctr1.config.UserNsCtr = ctr2.config.ID
	ctr2.config.IPCNsCtr = ctr3.config.ID
	ctr3.config.NetNsCtr = ctr1.config.ID

	_, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
	assert.Error(t, err)
}

func TestBuildContainerGraphThreeContainersNoCycle(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)
	ctr3, err := getTestCtrN("3", manager)
	assert.NoError(t, err)
	ctr1.config.UserNsCtr = ctr2.config.ID
	ctr1.config.NetNsCtr = ctr3.config.ID
	ctr2.config.IPCNsCtr = ctr3.config.ID

	graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3})
	assert.NoError(t, err)
	assert.Equal(t, 3, len(graph.nodes))
	assert.Equal(t, 1, len(graph.noDepNodes))
	assert.Equal(t, 1, len(graph.notDependedOnNodes))

	assert.Equal(t, ctr3.ID(), graph.noDepNodes[0].id)
	assert.Equal(t, ctr1.ID(), graph.notDependedOnNodes[ctr1.ID()].id)
}

func TestBuildContainerGraphFourContainersNoEdges(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)
	ctr3, err := getTestCtrN("3", manager)
	assert.NoError(t, err)
	ctr4, err := getTestCtrN("4", manager)
	assert.NoError(t, err)

	graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
	assert.NoError(t, err)
	assert.Equal(t, 4, len(graph.nodes))
	assert.Equal(t, 4, len(graph.noDepNodes))
	assert.Equal(t, 4, len(graph.notDependedOnNodes))

	node1, ok := graph.nodes[ctr1.ID()]
	assert.True(t, ok)
	assert.Equal(t, ctr1.ID(), node1.id)

	node2, ok := graph.nodes[ctr2.ID()]
	assert.True(t, ok)
	assert.Equal(t, ctr2.ID(), node2.id)

	node3, ok := graph.nodes[ctr3.ID()]
	assert.True(t, ok)
	assert.Equal(t, ctr3.ID(), node3.id)

	node4, ok := graph.nodes[ctr4.ID()]
	assert.True(t, ok)
	assert.Equal(t, ctr4.ID(), node4.id)
}

func TestBuildContainerGraphFourContainersTwoInCycle(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)
	ctr3, err := getTestCtrN("3", manager)
	assert.NoError(t, err)
	ctr4, err := getTestCtrN("4", manager)
	assert.NoError(t, err)

	ctr1.config.IPCNsCtr = ctr2.config.ID
	ctr2.config.UserNsCtr = ctr1.config.ID

	_, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
	assert.Error(t, err)
}

func TestBuildContainerGraphFourContainersAllInCycle(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)
	ctr3, err := getTestCtrN("3", manager)
	assert.NoError(t, err)
	ctr4, err := getTestCtrN("4", manager)
	assert.NoError(t, err)

	ctr1.config.IPCNsCtr = ctr2.config.ID
	ctr2.config.UserNsCtr = ctr3.config.ID
	ctr3.config.NetNsCtr = ctr4.config.ID
	ctr4.config.UTSNsCtr = ctr1.config.ID

	_, err = BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
	assert.Error(t, err)
}

func TestBuildContainerGraphFourContainersNoneInCycle(t *testing.T) {
	manager, err := lock.NewInMemoryManager(16)
	if err != nil {
		t.Fatalf("Error setting up locks: %v", err)
	}

	ctr1, err := getTestCtr1(manager)
	assert.NoError(t, err)
	ctr2, err := getTestCtr2(manager)
	assert.NoError(t, err)
	ctr3, err := getTestCtrN("3", manager)
	assert.NoError(t, err)
	ctr4, err := getTestCtrN("4", manager)
	assert.NoError(t, err)

	ctr1.config.IPCNsCtr = ctr2.config.ID
	ctr1.config.NetNsCtr = ctr3.config.ID
	ctr2.config.UserNsCtr = ctr3.config.ID

	graph, err := BuildContainerGraph([]*Container{ctr1, ctr2, ctr3, ctr4})
	assert.NoError(t, err)
	assert.Equal(t, 4, len(graph.nodes))
	assert.Equal(t, 2, len(graph.noDepNodes))
	assert.Equal(t, 2, len(graph.notDependedOnNodes))
}
