1# SPDX-License-Identifier: GPL-2.0
2# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3
4""" Test for bind command """
5
6import re
7import pytest
8
9def in_tree(response, name, uclass, drv, depth, last_child):
10    """A helper function to confirm contents of the device tree """
11    lines = [x.strip() for x in response.splitlines()]
12    leaf = ''
13    if depth != 0:
14        leaf = '   ' + '    ' * (depth - 1)
15        if not last_child:
16            leaf = leaf + r'\|'
17        else:
18            leaf = leaf + '`'
19
20    leaf = leaf + '-- ' + name
21    line = (r' *{:10.10} *[0-9]*  \[ [ +] \]   {:20.20}  [` |]{}$'
22            .format(uclass, drv, leaf))
23    prog = re.compile(line)
24    for l in lines:
25        if prog.match(l):
26            return True
27    return False
28
29@pytest.mark.boardspec('sandbox')
30@pytest.mark.buildconfigspec('cmd_bind')
31def test_bind_unbind_with_node(ubman):
32    """Test the bind and unbind commands of a node
33
34    Verify that the dm tree output contains some expected nodes, and then bind
35    and unbind a USB via node device while verifying that the dm tree output
36    matches the expected values at each step.
37    """
38    tree = ubman.run_command('dm tree')
39    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
40    assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
41    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
42
43    #bind usb_ether driver (which has no compatible) to usb@1 node.
44    ##New entry usb_ether should appear in the dm tree
45    response = ubman.run_command('bind  /usb@1 usb_ether')
46    assert response == ''
47    tree = ubman.run_command('dm tree')
48    assert in_tree(tree, 'usb@1', 'ethernet', 'usb_ether', 1, True)
49
50    #Unbind child #1. No error expected and all devices should be there except for bind-test-child1
51    response = ubman.run_command('unbind  /bind-test/bind-test-child1')
52    assert response == ''
53    tree = ubman.run_command('dm tree')
54    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
55    assert 'bind-test-child1' not in tree
56    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
57
58    #bind child #1. No error expected and all devices should be there
59    response = ubman.run_command('bind  /bind-test/bind-test-child1 phy_sandbox')
60    assert response == ''
61    tree = ubman.run_command('dm tree')
62    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
63    assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
64    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, False)
65
66    #Unbind child #2. No error expected and all devices should be there except for bind-test-child2
67    response = ubman.run_command('unbind  /bind-test/bind-test-child2')
68    assert response == ''
69    tree = ubman.run_command('dm tree')
70    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
71    assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
72    assert 'bind-test-child2' not in tree
73
74
75    #Bind child #2. No error expected and all devices should be there
76    response = ubman.run_command('bind /bind-test/bind-test-child2 simple_bus')
77    assert response == ''
78    tree = ubman.run_command('dm tree')
79    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
80    assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
81    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
82
83    #Unbind parent. No error expected. All devices should be removed and unbound
84    response = ubman.run_command('unbind  /bind-test')
85    assert response == ''
86    tree = ubman.run_command('dm tree')
87    assert 'bind-test' not in tree
88    assert 'bind-test-child1' not in tree
89    assert 'bind-test-child2' not in tree
90
91    #try binding invalid node with valid driver
92    response = ubman.run_command('bind  /not-a-valid-node simple_bus')
93    assert response != ''
94    tree = ubman.run_command('dm tree')
95    assert 'not-a-valid-node' not in tree
96
97    #try binding valid node with invalid driver
98    response = ubman.run_command('bind  /bind-test not_a_driver')
99    assert response != ''
100    tree = ubman.run_command('dm tree')
101    assert 'bind-test' not in tree
102
103    #bind /bind-test. Device should come up as well as its children
104    response = ubman.run_command('bind  /bind-test simple_bus')
105    assert response == ''
106    tree = ubman.run_command('dm tree')
107    assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
108    assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
109    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
110
111    response = ubman.run_command('unbind  /bind-test')
112    assert response == ''
113
114def get_next_line(tree, name):
115    """A helper function to strip content out of dm tree output"""
116    treelines = [x.strip() for x in tree.splitlines() if x.strip()]
117    child_line = ''
118    for idx, line in enumerate(treelines):
119        if '-- ' + name in line:
120            try:
121                child_line = treelines[idx+1]
122            except:
123                pass
124            break
125    return child_line
126
127@pytest.mark.boardspec('sandbox')
128@pytest.mark.buildconfigspec('cmd_bind')
129@pytest.mark.singlethread
130def test_bind_unbind_with_uclass(ubman):
131    """Test the bind and unbind commands of a class
132
133    Bind and unbind the simple_bus class while verifying that the dm tree
134    output matches the expected values at each step.
135    """
136    #bind /bind-test
137    response = ubman.run_command('bind  /bind-test simple_bus')
138    assert response == ''
139
140    #make sure bind-test-child2 is there and get its uclass/index pair
141    tree = ubman.run_command('dm tree')
142    child2_line = [x.strip() for x in tree.splitlines() if '-- bind-test-child2' in x]
143    assert len(child2_line) == 1
144
145    child2_uclass = child2_line[0].split()[0]
146    child2_index = int(child2_line[0].split()[1])
147
148    #bind simple_bus as a child of bind-test-child2
149    response = ubman.run_command(
150                    'bind  {} {} simple_bus'.format(child2_uclass, child2_index))
151
152    #check that the child is there and its uclass/index pair is right
153    tree = ubman.run_command('dm tree')
154
155    child_of_child2_line = get_next_line(tree, 'bind-test-child2')
156    assert child_of_child2_line
157    child_of_child2_index = int(child_of_child2_line.split()[1])
158    assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
159    assert child_of_child2_index == child2_index + 1
160
161    #unbind the child and check it has been removed
162    response = ubman.run_command('unbind  simple_bus {}'.format(child_of_child2_index))
163    assert response == ''
164    tree = ubman.run_command('dm tree')
165    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
166    assert not in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
167    child_of_child2_line = get_next_line(tree, 'bind-test-child2')
168    assert child_of_child2_line == ''
169
170    #bind simple_bus as a child of bind-test-child2
171    response = ubman.run_command(
172                    'bind  {} {} simple_bus'.format(child2_uclass, child2_index))
173
174    #check that the child is there and its uclass/index pair is right
175    tree = ubman.run_command('dm tree')
176    treelines = [x.strip() for x in tree.splitlines() if x.strip()]
177
178    child_of_child2_line = get_next_line(tree, 'bind-test-child2')
179    assert child_of_child2_line
180    child_of_child2_index = int(child_of_child2_line.split()[1])
181    assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
182    assert child_of_child2_index == child2_index + 1
183
184    #unbind the child and check it has been removed
185    response = ubman.run_command(
186                    'unbind  {} {} simple_bus'.format(child2_uclass, child2_index))
187    assert response == ''
188
189    tree = ubman.run_command('dm tree')
190    assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
191
192    child_of_child2_line = get_next_line(tree, 'bind-test-child2')
193    assert child_of_child2_line == ''
194
195    #unbind the child again and check it doesn't change the tree
196    tree_old = ubman.run_command('dm tree')
197    response = ubman.run_command(
198                    'unbind  {} {} simple_bus'.format(child2_uclass, child2_index))
199    tree_new = ubman.run_command('dm tree')
200
201    assert response == ''
202    assert tree_old == tree_new
203
204    response = ubman.run_command('unbind  /bind-test')
205    assert response == ''
206