4 from collections
import defaultdict
, namedtuple
6 from wptrunner
.wptmanifest
.serializer
import serialize
7 from wptrunner
.wptmanifest
.backends
import base
8 from wptrunner
.wptmanifest
.node
import KeyValueNode
10 here
= os
.path
.dirname(__file__
)
11 logger
= logging
.getLogger(__name__
)
14 class Compiler(base
.Compiler
):
15 def visit_KeyValueNode(self
, node
):
18 for child
in node
.children
:
19 values
.append(self
.visit(child
))
21 self
.output_node
.set(key_name
, values
)
23 def visit_ConditionalNode(self
, node
):
24 assert len(node
.children
) == 2
25 # For conditional nodes, just return the subtree
26 return serialize(node
.children
[0]), self
.visit(node
.children
[1])
28 def visit_UnaryExpressionNode(self
, node
):
29 raise NotImplementedError
31 def visit_BinaryExpressionNode(self
, node
):
32 raise NotImplementedError
34 def visit_UnaryOperatorNode(self
, node
):
35 raise NotImplementedError
37 def visit_BinaryOperatorNode(self
, node
):
38 raise NotImplementedError
41 class ExpectedManifest(base
.ManifestItem
):
42 def __init__(self
, node
):
43 """Object representing all the tests in a particular manifest"""
44 base
.ManifestItem
.__init
__(self
, node
)
47 def append(self
, child
):
48 """Add a test to the manifest"""
49 base
.ManifestItem
.append(self
, child
)
50 self
.child_map
[child
.name
] = child
52 def insert(self
, child
):
54 self
.node
.append(child
.node
)
56 def delete(self
, child
):
57 del self
.child_map
[child
.name
]
62 class TestManifestItem(ExpectedManifest
):
63 def set_expected(self
, other_manifest
):
64 for item
in self
.node
.children
:
65 if isinstance(item
, KeyValueNode
) and item
.data
== "expected":
66 assert "expected" in self
._data
68 del self
._data
["expected"]
71 for item
in other_manifest
.node
.children
:
72 if isinstance(item
, KeyValueNode
) and item
.data
== "expected":
73 assert "expected" in other_manifest
._data
75 self
.node
.children
.insert(0, item
)
76 self
._data
["expected"] = other_manifest
._data
.pop("expected")
80 def data_cls_getter(output_node
, visited_node
):
81 # visited_node is intentionally unused
82 if output_node
is None:
83 return ExpectedManifest
84 if isinstance(output_node
, ExpectedManifest
):
85 return TestManifestItem
89 def compile(stream
, data_cls_getter
=None, **kwargs
):
90 return base
.compile(Compiler
,
92 data_cls_getter
=data_cls_getter
,
96 def get_manifest(manifest_path
):
97 """Get the ExpectedManifest for a particular manifest path"""
99 with
open(manifest_path
) as f
:
101 data_cls_getter
=data_cls_getter
)
106 def indent(str_data
, indent
=2):
108 for line
in str_data
.splitlines():
109 rv
.append("%s%s" % (" " * indent
, line
))
113 class Differences(object):
119 def __nonzero__(self
):
120 return bool(self
.added
or self
.deleted
or self
.modified
)
124 for item
in self
.modified
:
125 if isinstance(item
, TestModified
):
126 modified
.append(" %s\n %s\n%s" % (item
[0], item
[1], indent(str(item
[2]), 4)))
128 assert isinstance(item
, ExpectedModified
)
129 modified
.append(" %s\n %s %s" % item
)
130 return "Added:\n%s\nDeleted:\n%s\nModified:\n%s\n" % (
131 "\n".join(" %s:\n %s" % item
for item
in self
.added
),
132 "\n".join(" %s" % item
for item
in self
.deleted
),
136 TestModified
= namedtuple("TestModified", ["test", "test_manifest", "differences"])
139 ExpectedModified
= namedtuple("ExpectedModified", ["test", "ancestor_manifest", "new_manifest"])
142 def compare_test(test
, ancestor_manifest
, new_manifest
):
143 changes
= Differences()
145 compare_expected(changes
, None, ancestor_manifest
, new_manifest
)
147 for subtest
, ancestor_subtest_manifest
in ancestor_manifest
.child_map
.iteritems():
148 compare_expected(changes
, subtest
, ancestor_subtest_manifest
,
149 new_manifest
.child_map
.get(subtest
))
151 for subtest
, subtest_manifest
in new_manifest
.child_map
.iteritems():
152 if subtest
not in ancestor_manifest
.child_map
:
153 changes
.added
.append((subtest
, subtest_manifest
))
158 def compare_expected(changes
, subtest
, ancestor_manifest
, new_manifest
):
159 if (not (ancestor_manifest
and ancestor_manifest
.has_key("expected")) and
160 (new_manifest
and new_manifest
.has_key("expected"))):
161 changes
.modified
.append(ExpectedModified(subtest
, ancestor_manifest
, new_manifest
))
162 elif (ancestor_manifest
and ancestor_manifest
.has_key("expected") and
163 not (new_manifest
and new_manifest
.has_key("expected"))):
164 changes
.deleted
.append(subtest
)
165 elif (ancestor_manifest
and ancestor_manifest
.has_key("expected") and
166 new_manifest
and new_manifest
.has_key("expected")):
167 old_expected
= ancestor_manifest
.get("expected")
168 new_expected
= new_manifest
.get("expected")
169 if expected_values_changed(old_expected
, new_expected
):
170 changes
.modified
.append(ExpectedModified(subtest
, ancestor_manifest
, new_manifest
))
173 def expected_values_changed(old_expected
, new_expected
):
174 if len(old_expected
) != len(new_expected
):
179 for dest
, cond_lines
in [(old_dict
, old_expected
), (new_dict
, new_expected
)]:
180 for cond_line
in cond_lines
:
181 if isinstance(cond_line
, tuple):
182 condition
, value
= cond_line
186 dest
[condition
] = value
188 return new_dict
!= old_dict
191 def record_changes(ancestor_manifest
, new_manifest
):
192 changes
= Differences()
194 for test
, test_manifest
in new_manifest
.child_map
.iteritems():
195 if test
not in ancestor_manifest
.child_map
:
196 changes
.added
.append((test
, test_manifest
))
198 ancestor_test_manifest
= ancestor_manifest
.child_map
[test
]
199 test_differences
= compare_test(test
,
200 ancestor_test_manifest
,
203 changes
.modified
.append(TestModified(test
, test_manifest
, test_differences
))
205 for test
, test_manifest
in ancestor_manifest
.child_map
.iteritems():
206 if test
not in new_manifest
.child_map
:
207 changes
.deleted
.append(test
)
212 def apply_changes(current_manifest
, changes
):
213 for test
, test_manifest
in changes
.added
:
214 if test
in current_manifest
.child_map
:
215 current_manifest
.delete(current_manifest
.child_map
[test
])
216 current_manifest
.insert(test_manifest
)
218 for test
in changes
.deleted
:
219 if test
in current_manifest
.child_map
:
220 current_manifest
.delete(current_manifest
.child_map
[test
])
222 for item
in changes
.modified
:
223 if isinstance(item
, TestModified
):
224 test
, new_manifest
, test_changes
= item
225 if test
in current_manifest
.child_map
:
226 apply_changes(current_manifest
.child_map
[test
], test_changes
)
228 current_manifest
.insert(new_manifest
)
230 assert isinstance(item
, ExpectedModified
)
231 subtest
, ancestor_manifest
, new_manifest
= item
233 current_manifest
.set_expected(new_manifest
)
234 elif subtest
in current_manifest
.child_map
:
235 current_manifest
.child_map
[subtest
].set_expected(new_manifest
)
237 current_manifest
.insert(new_manifest
)
241 parser
= argparse
.ArgumentParser()
242 parser
.add_argument("ancestor")
243 parser
.add_argument("current")
244 parser
.add_argument("new")
245 parser
.add_argument("dest", nargs
="?")
249 def get_parser_mergetool():
250 parser
= argparse
.ArgumentParser()
251 parser
.add_argument("--no-overwrite", dest
="overwrite", action
="store_false")
256 def make_changes(ancestor_manifest
, current_manifest
, new_manifest
):
257 changes
= record_changes(ancestor_manifest
, new_manifest
)
258 apply_changes(current_manifest
, changes
)
260 return serialize(current_manifest
.node
)
263 def run(ancestor
, current
, new
, dest
):
264 ancestor_manifest
= get_manifest(ancestor
)
265 current_manifest
= get_manifest(current
)
266 new_manifest
= get_manifest(new
)
269 updated_current_str
= make_changes(ancestor_manifest
, current_manifest
, new_manifest
)
272 with
open(dest
, "wb") as f
:
273 f
.write(updated_current_str
)
275 print(updated_current_str
)