Add new UTs
continuous-integration/drone/push Build is failing Details

main
Andrey Ivanov 2024-10-10 18:07:03 +03:00
parent 17b4b34bd3
commit 4e7267762d
4 changed files with 750 additions and 29 deletions

12
.run/test all.run.xml Normal file
View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="test all" type="GoTestRunConfiguration" factoryName="Go Test">
<module name="tests" />
<working_directory value="$PROJECT_DIR$" />
<kind value="DIRECTORY" />
<package value="tests" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<framework value="gotest" />
<method v="2" />
</configuration>
</component>

View File

@ -13,6 +13,9 @@ func DecomposeTree(tree map[string]*WeightedNode, threshold uint16) ([]SubTree,
index := GetTreeIndex(tree) index := GetTreeIndex(tree)
// Ищем ветку подходящую под лимит // Ищем ветку подходящую под лимит
branchID, weight := GetBranch(tree, index, threshold) branchID, weight := GetBranch(tree, index, threshold)
if branchID == "" {
return nil, errors.New("[GetNodePath]: threshold too small")
}
// Определяем путь к найденной ветке // Определяем путь к найденной ветке
path, err := GetNodePath(tree, branchID) path, err := GetNodePath(tree, branchID)
@ -65,6 +68,9 @@ func GetBranch(tree map[string]*WeightedNode, index []TreeIndex, threshold uint1
break break
} }
} }
if branchID == "" {
return "", 0
}
return tree[branchID].node.ID, tree[branchID].weight return tree[branchID].node.ID, tree[branchID].weight
} }
@ -78,7 +84,7 @@ func GetNodePath(tree map[string]*WeightedNode, nodeID string) (string, error) {
} }
nodeID = tree[nodeID].parent.ID nodeID = tree[nodeID].parent.ID
path := tree[nodeID].node.Name path := tree[nodeID].node.Name
for { for path != "/" {
if tree[nodeID].parent.Name == "/" { if tree[nodeID].parent.Name == "/" {
path = "/" + path path = "/" + path
break break
@ -104,6 +110,54 @@ func RecalculateParents(tree map[string]*WeightedNode, currentID string, nodeWei
} }
} }
func GetNode(tree map[string]*WeightedNode, index []TreeIndex, threshold uint16) (string, string, error) {
// Для работы необходим актуальный индекс мапы, отсортированный по убыванию
var branchID string
for _, v := range index {
if v.weight < threshold {
branchID = v.id
break
}
}
if branchID == "" {
return "", "", errors.New("threshold too small")
}
currentID := tree[branchID].node.ID
nodeWeight := tree[branchID].weight
// GetNodePath возвращает полный путь ноды от корня
if _, ok := tree[currentID]; !ok {
return "", "", errors.New("node not exists")
}
if tree[currentID].parent == nil {
return tree[currentID].node.ID, "/", nil
}
currentID = tree[currentID].parent.ID
path := tree[currentID].node.Name
for {
if tree[currentID].parent.Name == "/" {
path = "/" + path
break
}
currentID = tree[currentID].parent.ID
path = tree[currentID].node.Name + "/" + path
}
// RecalculateParents вычитает вес вырезанной ветки из всех родительских веток
if tree[currentID].parent == nil {
return "", "", errors.New("it's the root node")
}
currentID = tree[currentID].parent.ID
for {
tree[currentID].weight -= nodeWeight
if tree[currentID].parent == nil {
break
}
currentID = tree[currentID].parent.ID
}
return tree[branchID].node.ID, path, nil
}
// CutBranch удаляет из дерева ветку со всеми потомками, а так же удаляет ссылку на себя из родительской ноды // CutBranch удаляет из дерева ветку со всеми потомками, а так же удаляет ссылку на себя из родительской ноды
func CutBranch(tree map[string]*WeightedNode, nodeID string) *Node { func CutBranch(tree map[string]*WeightedNode, nodeID string) *Node {
// Удаление ссылки на ноду из родительской ноды // Удаление ссылки на ноду из родительской ноды

View File

@ -7,17 +7,40 @@ import (
) )
func TestDecomposeTree(t *testing.T) { func TestDecomposeTree(t *testing.T) {
testTree := getSmallTree() smallTree := getSmallTree()
singleNodeTree := getSingleNodeTree()
deepTree := getDeepTree()
assimmetricTree := getAssimmetricTree()
for name, tt := range map[string]struct { for name, tt := range map[string]struct {
inputTree map[string]*WeightedNode inputTree map[string]*WeightedNode
inputThreshold uint16 inputThreshold uint16
outputParts []SubTree outputParts []SubTree
err string err string
}{ }{
"Normal":{ "Normal tree 3x3": {
inputTree: testTree.TreeWeighted, inputTree: smallTree.TreeWeighted,
inputThreshold: testTree.Threshold, inputThreshold: smallTree.Threshold,
outputParts: testTree.Parts, outputParts: smallTree.Parts,
},
"Single node short tree": {
inputTree: singleNodeTree.TreeWeighted,
inputThreshold: singleNodeTree.Threshold,
outputParts: singleNodeTree.Parts,
},
"Single node short tree with small threshold": {
inputTree: singleNodeTree.TreeWeighted,
inputThreshold: 10,
err: "[GetNodePath]: threshold too small",
},
"Single branch deep tree": {
inputTree: deepTree.TreeWeighted,
inputThreshold: deepTree.Threshold,
outputParts: deepTree.Parts,
},
"Asymmetric tree": {
inputTree: assimmetricTree.TreeWeighted,
inputThreshold: assimmetricTree.Threshold,
outputParts: assimmetricTree.Parts,
}, },
} { } {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {

View File

@ -467,3 +467,635 @@ func getSmallTree() testDecomposingData {
}, },
} }
} }
func getSingleNodeTree() testDecomposingData {
newTree := &Node{
ID: "1.0.0.0",
Name: "/",
Children: []*Node{},
}
return testDecomposingData{
TreeWeighted: newTree.WeightingTreeWithStack(),
Threshold: 200,
Parts: []SubTree{
{
path: "/",
tree: &Node{
ID: "1.0.0.0",
Name: "/",
Children: []*Node{},
},
},
},
}
}
func getDeepTree() testDecomposingData {
newTree := &Node{
ID: "1",
Name: "/",
Children: []*Node{
{
ID: "1.2",
Name: "folder2",
Children: []*Node{
{
ID: "1.2.3",
Name: "folder3",
Children: []*Node{
{
ID: "1.2.3.4",
Name: "folder4",
Children: []*Node{
{
ID: "1.2.3.4.5",
Name: "folder5",
Children: []*Node{
{
ID: "1.2.3.4.5.6",
Name: "folder6",
Children: []*Node{
{
ID: "1.2.3.4.5.6.7",
Name: "folder7",
Children: []*Node{
{
ID: "1.2.3.4.5.6.7.8",
Name: "folder8",
Children: []*Node{
{
ID: "1.2.3.4.5.6.7.8.9",
Name: "folder9",
Children: []*Node{
{
ID: "1.2.3.4.5.6.7.8.9.0",
Name: "folder0",
Children: []*Node{
{
ID: "1.2.3.4.5.6.7.8.9.0.A",
Name: "folderA",
Children: []*Node{},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
}
return testDecomposingData{
TreeWeighted: newTree.WeightingTreeWithStack(),
Threshold: 200,
Parts: []SubTree{
{
path: "/folder1/folder2/folder3/folder4/folder5/folder6/folder7/folder8",
tree: &Node{
ID: "1.2.3.4.5.6.7.8.9",
Name: "folder9",
Children: []*Node{
{
ID: "1.2.3.4.5.6.7.8.9.0",
Name: "folder0",
Children: []*Node{
{
ID: "1.2.3.4.5.6.7.8.9.0.A",
Name: "folderA",
Children: []*Node{},
},
},
},
},
},
},
{
path: "/folder1/folder2/folder3/folder4/folder5",
tree: &Node{
ID: "1.2.3.4.5.6",
Name: "folder6",
Children: []*Node{
{
ID: "1.2.3.4.5.6.7",
Name: "folder7",
Children: []*Node{
{
ID: "1.2.3.4.5.6.7.8",
Name: "folder8",
Children: []*Node{},
},
},
},
},
},
},
{
path: "/",
tree: &Node{
ID: "1.2",
Name: "folder2",
Children: []*Node{
{
ID: "1.2.3",
Name: "folder3",
Children: []*Node{
{
ID: "1.2.3.4",
Name: "folder4",
Children: []*Node{
{
ID: "1.2.3.4.5",
Name: "folder5",
Children: []*Node{},
},
},
},
},
},
},
},
},
{
path: "/",
tree: &Node{
ID: "1",
Name: "/",
Children: []*Node{},
},
},
},
}
}
func getAssimmetricTree() testDecomposingData {
newTree := &Node{
ID: "1.0.0.0",
Name: "/",
Children: []*Node{
{
ID: "1.1.0.0",
Name: "folder1",
Children: []*Node{
{
ID: "1.1.1.0",
Name: "folder1",
Children: []*Node{
{
ID: "1.1.1.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.1.1.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.1.1.3",
Name: "folder3",
Children: []*Node{},
},
},
},
{
ID: "1.1.2.0",
Name: "folder2",
Children: []*Node{
{
ID: "1.1.2.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.1.2.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.1.2.3",
Name: "folder3",
Children: []*Node{},
},
},
},
{
ID: "1.1.3.0",
Name: "folder3",
Children: []*Node{
{
ID: "1.1.3.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.1.3.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.1.3.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
},
{
ID: "1.2.0.0",
Name: "folder2",
Children: []*Node{
{
ID: "1.2.1.0",
Name: "folder1",
Children: []*Node{
{
ID: "1.2.1.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.2.1.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.2.1.3",
Name: "folder3",
Children: []*Node{},
},
},
},
{
ID: "1.2.2.0",
Name: "folder2",
Children: []*Node{
{
ID: "1.2.2.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.2.2.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.2.2.3",
Name: "folder3",
Children: []*Node{},
},
},
},
{
ID: "1.2.3.0",
Name: "folder3",
Children: []*Node{
{
ID: "1.2.3.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.2.3.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.2.3.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
},
{
ID: "1.3.0.0",
Name: "folder3",
Children: []*Node{
{
ID: "1.3.1.0",
Name: "folder1",
Children: []*Node{
{
ID: "1.3.1.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.3.1.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.3.1.3",
Name: "folder3",
Children: []*Node{},
},
},
},
{
ID: "1.3.2.0",
Name: "folder2",
Children: []*Node{
{
ID: "1.3.2.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.3.2.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.3.2.3",
Name: "folder3",
Children: []*Node{},
},
},
},
{
ID: "1.3.3.0",
Name: "folder3",
Children: []*Node{
{
ID: "1.3.3.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.3.3.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.3.3.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
},
},
}
return testDecomposingData{
TreeWeighted: newTree.WeightingTreeWithStack(),
Threshold: 200,
Parts: []SubTree{
{
path: "/folder1",
tree: &Node{
ID: "1.1.1.0",
Name: "folder1",
Children: []*Node{
{
ID: "1.1.1.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.1.1.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.1.1.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
{
path: "/folder1",
tree: &Node{
ID: "1.1.2.0",
Name: "folder2",
Children: []*Node{
{
ID: "1.1.2.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.1.2.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.1.2.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
{
path: "/folder1",
tree: &Node{
ID: "1.1.3.0",
Name: "folder3",
Children: []*Node{
{
ID: "1.1.3.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.1.3.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.1.3.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
{
path: "/folder2",
tree: &Node{
ID: "1.2.1.0",
Name: "folder1",
Children: []*Node{
{
ID: "1.2.1.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.2.1.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.2.1.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
{
path: "/folder2",
tree: &Node{
ID: "1.2.2.0",
Name: "folder2",
Children: []*Node{
{
ID: "1.2.2.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.2.2.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.2.2.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
{
path: "/folder2",
tree: &Node{
ID: "1.2.3.0",
Name: "folder3",
Children: []*Node{
{
ID: "1.2.3.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.2.3.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.2.3.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
{
path: "/folder3",
tree: &Node{
ID: "1.3.1.0",
Name: "folder1",
Children: []*Node{
{
ID: "1.3.1.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.3.1.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.3.1.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
{
path: "/folder3",
tree: &Node{
ID: "1.3.2.0",
Name: "folder2",
Children: []*Node{
{
ID: "1.3.2.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.3.2.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.3.2.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
{
path: "/folder3",
tree: &Node{
ID: "1.3.3.0",
Name: "folder3",
Children: []*Node{
{
ID: "1.3.3.1",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.3.3.2",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.3.3.3",
Name: "folder3",
Children: []*Node{},
},
},
},
},
{
path: "/",
tree: &Node{
ID: "1.0.0.0",
Name: "/",
Children: []*Node{
{
ID: "1.1.0.0",
Name: "folder1",
Children: []*Node{},
},
{
ID: "1.2.0.0",
Name: "folder2",
Children: []*Node{},
},
{
ID: "1.3.0.0",
Name: "folder3",
Children: []*Node{},
},
},
},
},
},
}
}