• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Test overlay."""
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import print_function
20
21import os
22import shutil
23import subprocess
24import tempfile
25import unittest
26from . import config
27from . import overlay
28import re
29
30
31class BindOverlayTest(unittest.TestCase):
32
33  def setUp(self):
34    self.source_dir = tempfile.mkdtemp()
35    self.destination_dir = tempfile.mkdtemp()
36    #
37    # base_dir/
38    #   base_project/
39    #     .git
40    # no_git_dir/
41    #   no_git_subdir1/
42    #     no_git_file1
43    #   no_git_subdir2/
44    #     no_git_file2
45    # overlays/
46    #   unittest1/
47    #     from_dir/
48    #       .git/
49    #     upper_subdir/
50    #       lower_subdir/
51    #         from_unittest1/
52    #           .git/
53    #     from_file
54    #   unittest2/
55    #     upper_subdir/
56    #       lower_subdir/
57    #         from_unittest2/
58    #           .git/
59    #   no_git_dir2/
60    #     no_git_subdir1/
61    #     no_git_subdir2/
62    #       .bindmount
63    #
64    os.mkdir(os.path.join(self.source_dir, 'base_dir'))
65    os.mkdir(os.path.join(self.source_dir, 'base_dir', 'base_project'))
66    os.mkdir(os.path.join(self.source_dir, 'base_dir', 'base_project', '.git'))
67    os.mkdir(os.path.join(self.source_dir, 'no_git_dir'))
68    os.mkdir(os.path.join(self.source_dir, 'no_git_dir', 'no_git_subdir1'))
69    open(os.path.join(self.source_dir,
70                      'no_git_dir', 'no_git_subdir1', 'no_git_file1'), 'a').close()
71    os.mkdir(os.path.join(self.source_dir, 'no_git_dir', 'no_git_subdir2'))
72    open(os.path.join(self.source_dir,
73                      'no_git_dir', 'no_git_subdir2', 'no_git_file2'), 'a').close()
74    os.mkdir(os.path.join(self.source_dir, 'overlays'))
75    os.mkdir(os.path.join(self.source_dir,
76                          'overlays', 'unittest1'))
77    os.mkdir(os.path.join(self.source_dir,
78                          'overlays', 'unittest1', 'from_dir'))
79    os.mkdir(os.path.join(self.source_dir,
80                          'overlays', 'unittest1', 'from_dir', '.git'))
81    os.mkdir(os.path.join(self.source_dir,
82                          'overlays', 'unittest1', 'upper_subdir'))
83    os.mkdir(os.path.join(self.source_dir,
84                          'overlays', 'unittest1', 'upper_subdir',
85                          'lower_subdir'))
86    os.mkdir(os.path.join(self.source_dir,
87                          'overlays', 'unittest1', 'upper_subdir',
88                          'lower_subdir', 'from_unittest1'))
89    os.mkdir(os.path.join(self.source_dir,
90                          'overlays', 'unittest1', 'upper_subdir',
91                          'lower_subdir', 'from_unittest1', '.git'))
92    os.symlink(
93            os.path.join(self.source_dir, 'overlays', 'unittest1',
94                'upper_subdir', 'lower_subdir'),
95            os.path.join(self.source_dir, 'overlays', 'unittest1',
96                'upper_subdir', 'subdir_symlink')
97            )
98    open(os.path.join(self.source_dir,
99                      'overlays', 'unittest1', 'from_file'), 'a').close()
100    os.mkdir(os.path.join(self.source_dir,
101                          'overlays', 'unittest2'))
102    os.mkdir(os.path.join(self.source_dir,
103                          'overlays', 'unittest2', 'upper_subdir'))
104    os.mkdir(os.path.join(self.source_dir,
105                          'overlays', 'unittest2', 'upper_subdir',
106                          'lower_subdir'))
107    os.mkdir(os.path.join(self.source_dir,
108                          'overlays', 'unittest2', 'upper_subdir',
109                          'lower_subdir', 'from_unittest2'))
110    os.mkdir(os.path.join(self.source_dir,
111                          'overlays', 'unittest2', 'upper_subdir',
112                          'lower_subdir', 'from_unittest2', '.git'))
113
114    os.mkdir(os.path.join(self.source_dir, 'overlays', 'no_git_dir2'))
115    os.mkdir(os.path.join(self.source_dir,
116                          'overlays', 'no_git_dir2', 'no_git_subdir1'))
117    os.mkdir(os.path.join(self.source_dir,
118                          'overlays', 'no_git_dir2', 'no_git_subdir2'))
119    open(os.path.join(self.source_dir,
120                      'overlays', 'no_git_dir2', 'no_git_subdir2', '.bindmount'),
121         'a').close()
122
123  def tearDown(self):
124    shutil.rmtree(self.source_dir)
125
126  def testValidTargetOverlayBinds(self):
127    with tempfile.NamedTemporaryFile('w+t') as test_config:
128      test_config.write(
129        '<?xml version="1.0" encoding="UTF-8" ?>'
130        '<config>'
131        '  <target name="unittest">'
132        '    <overlay name="unittest1"/>'
133        '    <build_config>'
134        '      <goal name="goal_name"/>'
135        '    </build_config>'
136        '  </target>'
137        '</config>'
138        )
139      test_config.flush()
140      o = overlay.BindOverlay(
141          cfg=config.factory(test_config.name),
142          build_target='unittest',
143          source_dir=self.source_dir)
144    self.assertIsNotNone(o)
145    bind_mounts = o.GetBindMounts()
146    bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir')
147    bind_destination = os.path.join(self.source_dir, 'from_dir')
148    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False))
149    self.assertIn(os.path.join(self.source_dir, 'base_dir', 'base_project'), bind_mounts)
150
151  def testValidTargetOverlayBindsAllowedProjects(self):
152    with tempfile.NamedTemporaryFile('w+t') as test_config, \
153        tempfile.NamedTemporaryFile('w+t') as test_allowed_projects:
154      test_config.write(
155        '<?xml version="1.0" encoding="UTF-8" ?>'
156        '<config>'
157        '  <target name="unittest">'
158        '    <overlay name="unittest1"/>'
159        '    <build_config allowed_projects_file="%s">'
160        '      <goal name="goal_name"/>'
161        '    </build_config>'
162        '  </target>'
163        '</config>' % test_allowed_projects.name
164        )
165      test_config.flush()
166      test_allowed_projects.write(
167        '<?xml version="1.0" encoding="UTF-8" ?>'
168        '<manifest>'
169        '  <project name="from_dir" path="overlays/unittest1/from_dir"/>'
170        '</manifest>'
171        )
172      test_allowed_projects.flush()
173      o = overlay.BindOverlay(
174          cfg=config.factory(test_config.name),
175          build_target='unittest',
176          source_dir=self.source_dir)
177    self.assertIsNotNone(o)
178    bind_mounts = o.GetBindMounts()
179    self.assertIn(os.path.join(self.source_dir, 'from_dir'), bind_mounts)
180    self.assertNotIn(os.path.join(self.source_dir, 'base_dir', 'base_project'), bind_mounts)
181
182  def testMultipleOverlays(self):
183    with tempfile.NamedTemporaryFile('w+t') as test_config:
184      test_config.write(
185        '<?xml version="1.0" encoding="UTF-8" ?>'
186        '<config>'
187        '  <target name="unittest">'
188        '    <overlay name="unittest1"/>'
189        '    <overlay name="unittest2"/>'
190        '    <build_config>'
191        '      <goal name="goal_name"/>'
192        '    </build_config>'
193        '  </target>'
194        '</config>'
195        )
196      test_config.flush()
197      o = overlay.BindOverlay(
198          cfg=config.factory(test_config.name),
199          build_target='unittest',
200          source_dir=self.source_dir)
201    self.assertIsNotNone(o)
202    bind_mounts = o.GetBindMounts()
203    bind_source = os.path.join(self.source_dir,
204      'overlays/unittest1/upper_subdir/lower_subdir/from_unittest1')
205    bind_destination = os.path.join(self.source_dir, 'upper_subdir/lower_subdir/from_unittest1')
206    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False))
207    bind_source = os.path.join(self.source_dir,
208      'overlays/unittest2/upper_subdir/lower_subdir/from_unittest2')
209    bind_destination = os.path.join(self.source_dir,
210      'upper_subdir/lower_subdir/from_unittest2')
211    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False))
212
213  def testMultipleOverlaysWithAllowlist(self):
214    with tempfile.NamedTemporaryFile('w+t') as test_config:
215      test_config.write(
216        '<?xml version="1.0" encoding="UTF-8" ?>'
217        '<config>'
218        '  <target name="unittest">'
219        '    <overlay name="unittest1"/>'
220        '    <overlay name="unittest2"/>'
221        '    <allow_readwrite path="overlays/unittest1/upper_subdir/lower_subdir/from_unittest1"/>'
222        '    <build_config>'
223        '      <goal name="goal_name"/>'
224        '    </build_config>'
225        '  </target>'
226        '</config>'
227        )
228      test_config.flush()
229      o = overlay.BindOverlay(
230          cfg=config.factory(test_config.name),
231          build_target='unittest',
232          source_dir=self.source_dir)
233    self.assertIsNotNone(o)
234    bind_mounts = o.GetBindMounts()
235    bind_source = os.path.join(self.source_dir,
236      'overlays/unittest1/upper_subdir/lower_subdir/from_unittest1')
237    bind_destination = os.path.join(self.source_dir, 'upper_subdir/lower_subdir/from_unittest1')
238    self.assertEqual(
239        bind_mounts[bind_destination],
240        overlay.BindMount(source_dir=bind_source, readonly=False, allows_replacement=False))
241    bind_source = os.path.join(self.source_dir,
242      'overlays/unittest2/upper_subdir/lower_subdir/from_unittest2')
243    bind_destination = os.path.join(self.source_dir,
244      'upper_subdir/lower_subdir/from_unittest2')
245    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False))
246
247  def testAllowReadWriteNoGitDir(self):
248    with tempfile.NamedTemporaryFile('w+t') as test_config:
249      test_config.write(
250        '<?xml version="1.0" encoding="UTF-8" ?>'
251        '<config>'
252        '  <target name="unittest">'
253        '    <overlay name="unittest1"/>'
254        '    <overlay name="unittest2"/>'
255        '    <allow_readwrite path="no_git_dir/no_git_subdir1"/>'
256        '    <build_config>'
257        '      <goal name="goal_name"/>'
258        '    </build_config>'
259        '  </target>'
260        '</config>'
261        )
262      test_config.flush()
263      o = overlay.BindOverlay(
264          cfg=config.factory(test_config.name),
265          build_target='unittest',
266          source_dir=self.source_dir)
267    self.assertIsNotNone(o)
268    bind_mounts = o.GetBindMounts()
269    bind_source = os.path.join(self.source_dir,
270      'no_git_dir/no_git_subdir1')
271    bind_destination = os.path.join(self.source_dir, 'no_git_dir/no_git_subdir1')
272    self.assertIn(bind_destination, bind_mounts)
273    self.assertEqual(
274        bind_mounts[bind_destination],
275        overlay.BindMount(source_dir=bind_source, readonly=False, allows_replacement=False))
276    bind_source = os.path.join(self.source_dir,
277      'no_git_dir/no_git_subdir2')
278    bind_destination = os.path.join(self.source_dir,
279      'no_git_dir/no_git_subdir2')
280    self.assertIn(bind_destination, bind_mounts)
281    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False))
282
283  def testValidOverlaidDir(self):
284    with tempfile.NamedTemporaryFile('w+t') as test_config:
285      test_config.write(
286        '<?xml version="1.0" encoding="UTF-8" ?>'
287        '<config>'
288        '  <target name="unittest">'
289        '    <overlay name="unittest1"/>'
290        '    <build_config>'
291        '      <goal name="goal_name"/>'
292        '    </build_config>'
293        '  </target>'
294        '</config>'
295        )
296      test_config.flush()
297      o = overlay.BindOverlay(
298          cfg=config.factory(test_config.name),
299          build_target='unittest',
300          source_dir=self.source_dir,
301          destination_dir=self.destination_dir)
302    self.assertIsNotNone(o)
303    bind_mounts = o.GetBindMounts()
304    bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir')
305    bind_destination = os.path.join(self.destination_dir, 'from_dir')
306    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False))
307
308  def testValidFilesystemViewDirectoryBind(self):
309    with tempfile.NamedTemporaryFile('w+t') as test_config:
310      test_config.write(
311        '<?xml version="1.0" encoding="UTF-8" ?>'
312        '<config>'
313        '  <target name="unittest">'
314        '    <view name="unittestview"/>'
315        '    <build_config>'
316        '      <goal name="goal_name"/>'
317        '    </build_config>'
318        '  </target>'
319        '  <view name="unittestview">'
320        '    <path source="overlays/unittest1/from_dir" '
321        '    destination="to_dir"/>'
322        '  </view>'
323        '</config>'
324        )
325      test_config.flush()
326      o = overlay.BindOverlay(
327          cfg=config.factory(test_config.name),
328          build_target='unittest',
329          source_dir=self.source_dir)
330    self.assertIsNotNone(o)
331    bind_mounts = o.GetBindMounts()
332    bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir')
333    bind_destination = os.path.join(self.source_dir, 'to_dir')
334    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False))
335
336  def testValidFilesystemViewFileBind(self):
337    with tempfile.NamedTemporaryFile('w+t') as test_config:
338      test_config.write(
339          '<?xml version="1.0" encoding="UTF-8" ?>'
340          '<config>'
341          '  <target name="unittest">'
342          '    <view name="unittestview"/>'
343          '    <build_config>'
344          '      <goal name="goal_name"/>'
345          '    </build_config>'
346          '  </target>'
347          '  <view name="unittestview">'
348          '    <path source="overlays/unittest1/from_file" '
349          '      destination="to_file"/>'
350          '  </view>'
351          '</config>'
352          )
353      test_config.flush()
354      o = overlay.BindOverlay(
355          cfg=config.factory(test_config.name),
356          build_target='unittest',
357          source_dir=self.source_dir)
358    self.assertIsNotNone(o)
359    bind_mounts = o.GetBindMounts()
360    bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_file')
361    bind_destination = os.path.join(self.source_dir, 'to_file')
362    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False))
363
364  def testInvalidTarget(self):
365    with tempfile.NamedTemporaryFile('w+t') as test_config:
366      test_config.write(
367        '<?xml version="1.0" encoding="UTF-8" ?>'
368        '<config>'
369        '  <target name="unittest">'
370        '    <overlay name="unittest1"/>'
371        '    <build_config>'
372        '      <goal name="goal_name"/>'
373        '    </build_config>'
374        '  </target>'
375        '</config>'
376        )
377      test_config.flush()
378      with self.assertRaises(KeyError):
379        overlay.BindOverlay(
380            cfg=config.factory(test_config.name),
381            build_target='unknown',
382            source_dir=self.source_dir)
383
384  def testExplicitBindMount(self):
385    with tempfile.NamedTemporaryFile('w+t') as test_config:
386      test_config.write(
387        '<?xml version="1.0" encoding="UTF-8" ?>'
388        '<config>'
389        '  <target name="target_name">'
390        '    <overlay name="no_git_dir2"/>'
391        '    <build_config>'
392        '      <goal name="goal_name"/>'
393        '    </build_config>'
394        '  </target>'
395        '</config>'
396        )
397      test_config.flush()
398      o = overlay.BindOverlay(
399          cfg=config.factory(test_config.name),
400          build_target='target_name',
401          source_dir=self.source_dir)
402    self.assertIsNotNone(o)
403    bind_mounts = o.GetBindMounts()
404
405    bind_source = os.path.join(self.source_dir, 'overlays/no_git_dir2/no_git_subdir1')
406    bind_destination = os.path.join(self.source_dir, 'no_git_subdir1')
407    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False))
408
409    bind_source = os.path.join(self.source_dir, 'overlays/no_git_dir2/no_git_subdir2')
410    bind_destination = os.path.join(self.source_dir, 'no_git_subdir2')
411    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False))
412
413  def testReplacementPath(self):
414    with tempfile.NamedTemporaryFile('w+t') as test_config:
415      test_config.write(
416        '<?xml version="1.0" encoding="UTF-8" ?>'
417        '<config>'
418        '  <target name="unittest">'
419        '    <overlay name="unittest1">'
420        '     <replacement_path path="from_dir"/>'
421        '    </overlay>'
422        '    <build_config>'
423        '      <goal name="goal_name"/>'
424        '    </build_config>'
425        '  </target>'
426        '</config>'
427        )
428      test_config.flush()
429      o = overlay.BindOverlay(
430            cfg=config.factory(test_config.name),
431            build_target='unittest',
432            source_dir=self.source_dir)
433    self.assertIsNotNone(o)
434    bind_mounts = o.GetBindMounts()
435    bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir')
436    bind_destination = os.path.join(self.source_dir, 'from_dir')
437    self.assertEqual(bind_mounts[bind_destination],
438                     overlay.BindMount(bind_source, True, True))
439
440if __name__ == '__main__':
441  unittest.main()
442