b201fe7f167de9989a49268114d95d77f55afd51
[WebKit-https.git] / WebDriverTests / imported / w3c / tools / wptrunner / wptrunner / update / sync.py
1 import fnmatch
2 import os
3 import re
4 import shutil
5 import sys
6 import uuid
7
8 from .. import testloader
9
10 from base import Step, StepRunner
11 from tree import Commit
12
13 here = os.path.abspath(os.path.split(__file__)[0])
14
15 bsd_license = """W3C 3-clause BSD License
16
17 Redistribution and use in source and binary forms, with or without
18 modification, are permitted provided that the following conditions are
19 met:
20
21 * Redistributions of works must retain the original copyright notice, this
22   list of conditions and the following disclaimer.
23
24 * Redistributions in binary form must reproduce the original copyright
25   notice, this list of conditions and the following disclaimer in the
26   documentation and/or other materials provided with the distribution.
27
28 * Neither the name of the W3C nor the names of its contributors may be
29   used to endorse or promote products derived from this work without
30   specific prior written permission.
31
32
33 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
34 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
37 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
38 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
41 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
43 POSSIBILITY OF SUCH DAMAGE.
44 """
45
46
47 def copy_wpt_tree(tree, dest, excludes=None, includes=None):
48     """Copy the working copy of a Tree to a destination directory.
49
50     :param tree: The Tree to copy.
51     :param dest: The destination directory"""
52     if os.path.exists(dest):
53         assert os.path.isdir(dest)
54
55     shutil.rmtree(dest)
56
57     os.mkdir(dest)
58
59     if excludes is None:
60         excludes = []
61
62     excludes = [re.compile(fnmatch.translate(item)) for item in excludes]
63
64     if includes is None:
65         includes = []
66
67     includes = [re.compile(fnmatch.translate(item)) for item in includes]
68
69     for tree_path in tree.paths():
70         if (any(item.match(tree_path) for item in excludes) and
71             not any(item.match(tree_path) for item in includes)):
72             continue
73
74         source_path = os.path.join(tree.root, tree_path)
75         dest_path = os.path.join(dest, tree_path)
76
77         dest_dir = os.path.split(dest_path)[0]
78         if not os.path.isdir(source_path):
79             if not os.path.exists(dest_dir):
80                 os.makedirs(dest_dir)
81             shutil.copy2(source_path, dest_path)
82
83     for source, destination in [("testharness_runner.html", ""),
84                                 ("testharnessreport.js", "resources/")]:
85         source_path = os.path.join(here, os.pardir, source)
86         dest_path = os.path.join(dest, destination, os.path.split(source)[1])
87         shutil.copy2(source_path, dest_path)
88
89     add_license(dest)
90
91
92 def add_license(dest):
93     """Write the bsd license string to a LICENSE file.
94
95     :param dest: Directory in which to place the LICENSE file."""
96     with open(os.path.join(dest, "LICENSE"), "w") as f:
97         f.write(bsd_license)
98
99
100 class UpdateCheckout(Step):
101     """Pull changes from upstream into the local sync tree."""
102
103     provides = ["local_branch"]
104
105     def create(self, state):
106         sync_tree = state.sync_tree
107         state.local_branch = uuid.uuid4().hex
108         sync_tree.update(state.sync["remote_url"],
109                          state.sync["branch"],
110                          state.local_branch)
111         sync_path = os.path.abspath(sync_tree.root)
112         if not sync_path in sys.path:
113             from update import setup_paths
114             setup_paths(sync_path)
115
116     def restore(self, state):
117         assert os.path.abspath(state.sync_tree.root) in sys.path
118         Step.restore(self, state)
119
120
121 class GetSyncTargetCommit(Step):
122     """Find the commit that we will sync to."""
123
124     provides = ["sync_commit"]
125
126     def create(self, state):
127         if state.target_rev is None:
128             #Use upstream branch HEAD as the base commit
129             state.sync_commit = state.sync_tree.get_remote_sha1(state.sync["remote_url"],
130                                                                 state.sync["branch"])
131         else:
132             state.sync_commit = Commit(state.sync_tree, state.rev)
133
134         state.sync_tree.checkout(state.sync_commit.sha1, state.local_branch, force=True)
135         self.logger.debug("New base commit is %s" % state.sync_commit.sha1)
136
137
138 class LoadManifest(Step):
139     """Load the test manifest"""
140
141     provides = ["manifest_path", "test_manifest"]
142
143     def create(self, state):
144         from manifest import manifest
145         state.manifest_path = os.path.join(state.metadata_path, "MANIFEST.json")
146         state.test_manifest = manifest.Manifest("/")
147
148
149 class UpdateManifest(Step):
150     """Update the manifest to match the tests in the sync tree checkout"""
151
152     def create(self, state):
153         from manifest import manifest, update
154         update.update(state.sync["path"], state.test_manifest)
155         manifest.write(state.test_manifest, state.manifest_path)
156
157
158 class CopyWorkTree(Step):
159     """Copy the sync tree over to the destination in the local tree"""
160
161     def create(self, state):
162         copy_wpt_tree(state.sync_tree,
163                       state.tests_path,
164                       excludes=state.path_excludes,
165                       includes=state.path_includes)
166
167
168 class CreateSyncPatch(Step):
169     """Add the updated test files to a commit/patch in the local tree."""
170
171     def create(self, state):
172         if not state.patch:
173             return
174
175         local_tree = state.local_tree
176         sync_tree = state.sync_tree
177
178         local_tree.create_patch("web-platform-tests_update_%s" % sync_tree.rev,
179                                 "Update %s to revision %s" % (state.suite_name, sync_tree.rev))
180         local_tree.add_new(os.path.relpath(state.tests_path,
181                                            local_tree.root))
182         updated = local_tree.update_patch(include=[state.tests_path,
183                                                    state.metadata_path])
184         local_tree.commit_patch()
185
186         if not updated:
187             self.logger.info("Nothing to sync")
188
189
190 class SyncFromUpstreamRunner(StepRunner):
191     """(Sub)Runner for doing an upstream sync"""
192     steps = [UpdateCheckout,
193              GetSyncTargetCommit,
194              LoadManifest,
195              UpdateManifest,
196              CopyWorkTree,
197              CreateSyncPatch]