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 overlay
27import re
28
29
30class BindOverlayTest(unittest.TestCase):
31
32  def setUp(self):
33    self.source_dir = tempfile.mkdtemp()
34    self.destination_dir = tempfile.mkdtemp()
35    os.mkdir(os.path.join(self.source_dir, 'base_dir'))
36    os.mkdir(os.path.join(self.source_dir, 'base_dir', 'base_project'))
37    os.mkdir(os.path.join(self.source_dir, 'base_dir', 'base_project', '.git'))
38    os.mkdir(os.path.join(self.source_dir, 'overlays'))
39    os.mkdir(os.path.join(self.source_dir,
40                          'overlays', 'unittest1'))
41    os.mkdir(os.path.join(self.source_dir,
42                          'overlays', 'unittest1', 'from_dir'))
43    os.mkdir(os.path.join(self.source_dir,
44                          'overlays', 'unittest1', 'from_dir', '.git'))
45    os.mkdir(os.path.join(self.source_dir,
46                          'overlays', 'unittest1', 'upper_subdir'))
47    os.mkdir(os.path.join(self.source_dir,
48                          'overlays', 'unittest1', 'upper_subdir',
49                          'lower_subdir'))
50    os.mkdir(os.path.join(self.source_dir,
51                          'overlays', 'unittest1', 'upper_subdir',
52                          'lower_subdir', 'from_unittest1'))
53    os.mkdir(os.path.join(self.source_dir,
54                          'overlays', 'unittest1', 'upper_subdir',
55                          'lower_subdir', 'from_unittest1', '.git'))
56    os.symlink(
57            os.path.join(self.source_dir, 'overlays', 'unittest1',
58                'upper_subdir', 'lower_subdir'),
59            os.path.join(self.source_dir, 'overlays', 'unittest1',
60                'upper_subdir', 'subdir_symlink')
61            )
62    open(os.path.join(self.source_dir,
63                      'overlays', 'unittest1', 'from_file'), 'a').close()
64    os.mkdir(os.path.join(self.source_dir,
65                          'overlays', 'unittest2'))
66    os.mkdir(os.path.join(self.source_dir,
67                          'overlays', 'unittest2', 'upper_subdir'))
68    os.mkdir(os.path.join(self.source_dir,
69                          'overlays', 'unittest2', 'upper_subdir',
70                          'lower_subdir'))
71    os.mkdir(os.path.join(self.source_dir,
72                          'overlays', 'unittest2', 'upper_subdir',
73                          'lower_subdir', 'from_unittest2'))
74    os.mkdir(os.path.join(self.source_dir,
75                          'overlays', 'unittest2', 'upper_subdir',
76                          'lower_subdir', 'from_unittest2', '.git'))
77
78  def tearDown(self):
79    shutil.rmtree(self.source_dir)
80
81  def testValidTargetOverlayBinds(self):
82    with tempfile.NamedTemporaryFile('w+t') as test_config:
83      test_config.write(
84        '<?xml version="1.0" encoding="UTF-8" ?>'
85        '<config>'
86        '  <target name="unittest">'
87        '    <overlay name="unittest1"/>'
88        '  </target>'
89        '</config>'
90        )
91      test_config.flush()
92      o = overlay.BindOverlay(
93          config_file=test_config.name,
94          target='unittest',
95          source_dir=self.source_dir)
96    self.assertIsNotNone(o)
97    bind_mounts = o.GetBindMounts()
98    bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir')
99    bind_destination = os.path.join(self.source_dir, 'from_dir')
100    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, False))
101
102  def testMultipleOverlays(self):
103    with tempfile.NamedTemporaryFile('w+t') as test_config:
104      test_config.write(
105        '<?xml version="1.0" encoding="UTF-8" ?>'
106        '<config>'
107        '  <target name="unittest">'
108        '    <overlay name="unittest1"/>'
109        '    <overlay name="unittest2"/>'
110        '  </target>'
111        '</config>'
112        )
113      test_config.flush()
114      o = overlay.BindOverlay(
115          config_file=test_config.name,
116          target='unittest',
117          source_dir=self.source_dir)
118    self.assertIsNotNone(o)
119    bind_mounts = o.GetBindMounts()
120    bind_source = os.path.join(self.source_dir,
121      'overlays/unittest1/upper_subdir/lower_subdir/from_unittest1')
122    bind_destination = os.path.join(self.source_dir, 'upper_subdir/lower_subdir/from_unittest1')
123    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, False))
124    bind_source = os.path.join(self.source_dir,
125      'overlays/unittest2/upper_subdir/lower_subdir/from_unittest2')
126    bind_destination = os.path.join(self.source_dir,
127      'upper_subdir/lower_subdir/from_unittest2')
128    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, False))
129
130  def testMultipleOverlaysWithWhitelist(self):
131    with tempfile.NamedTemporaryFile('w+t') as test_config:
132      test_config.write(
133        '<?xml version="1.0" encoding="UTF-8" ?>'
134        '<config>'
135        '  <target name="unittest">'
136        '    <overlay name="unittest1"/>'
137        '    <overlay name="unittest2"/>'
138        '  </target>'
139        '</config>'
140        )
141      test_config.flush()
142      rw_whitelist = set('overlays/unittest1/uppser_subdir/lower_subdir/from_unittest1')
143      o = overlay.BindOverlay(
144          config_file=test_config.name,
145          target='unittest',
146          source_dir=self.source_dir)
147    self.assertIsNotNone(o)
148    bind_mounts = o.GetBindMounts()
149    bind_source = os.path.join(self.source_dir,
150      'overlays/unittest1/upper_subdir/lower_subdir/from_unittest1')
151    bind_destination = os.path.join(self.source_dir, 'upper_subdir/lower_subdir/from_unittest1')
152    self.assertEqual(
153        bind_mounts[bind_destination],
154        overlay.BindMount(source_dir=bind_source, readonly=False))
155    bind_source = os.path.join(self.source_dir,
156      'overlays/unittest2/upper_subdir/lower_subdir/from_unittest2')
157    bind_destination = os.path.join(self.source_dir,
158      'upper_subdir/lower_subdir/from_unittest2')
159    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, False))
160
161  def testValidOverlaidDir(self):
162    with tempfile.NamedTemporaryFile('w+t') as test_config:
163      test_config.write(
164        '<?xml version="1.0" encoding="UTF-8" ?>'
165        '<config>'
166        '  <target name="unittest">'
167        '    <overlay name="unittest1"/>'
168        '  </target>'
169        '</config>'
170        )
171      test_config.flush()
172      o = overlay.BindOverlay(
173          config_file=test_config.name,
174          target='unittest',
175          source_dir=self.source_dir,
176          destination_dir=self.destination_dir)
177    self.assertIsNotNone(o)
178    bind_mounts = o.GetBindMounts()
179    bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir')
180    bind_destination = os.path.join(self.destination_dir, 'from_dir')
181    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, False))
182
183  def testValidFilesystemViewDirectoryBind(self):
184    with tempfile.NamedTemporaryFile('w+t') as test_config:
185      test_config.write(
186        '<?xml version="1.0" encoding="UTF-8" ?>'
187        '<config>'
188        '  <target name="unittest">'
189        '    <view name="unittestview"/>'
190        '  </target>'
191        '  <view name="unittestview">'
192        '    <path source="overlays/unittest1/from_dir" '
193        '    destination="to_dir"/>'
194        '  </view>'
195        '</config>'
196        )
197      test_config.flush()
198      o = overlay.BindOverlay(
199          config_file=test_config.name,
200          target='unittest',
201          source_dir=self.source_dir)
202    self.assertIsNotNone(o)
203    bind_mounts = o.GetBindMounts()
204    bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir')
205    bind_destination = os.path.join(self.source_dir, 'to_dir')
206    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, False))
207
208  def testValidFilesystemViewFileBind(self):
209    with tempfile.NamedTemporaryFile('w+t') as test_config:
210      test_config.write(
211          '<?xml version="1.0" encoding="UTF-8" ?>'
212          '<config>'
213          '  <target name="unittest">'
214          '    <view name="unittestview"/>'
215          '  </target>'
216          '  <view name="unittestview">'
217          '    <path source="overlays/unittest1/from_file" '
218          '      destination="to_file"/>'
219          '  </view>'
220          '</config>'
221          )
222      test_config.flush()
223      o = overlay.BindOverlay(
224          config_file=test_config.name,
225          target='unittest',
226          source_dir=self.source_dir)
227    self.assertIsNotNone(o)
228    bind_mounts = o.GetBindMounts()
229    bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_file')
230    bind_destination = os.path.join(self.source_dir, 'to_file')
231    self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, False))
232
233  def testInvalidTarget(self):
234    with tempfile.NamedTemporaryFile('w+t') as test_config:
235      test_config.write(
236        '<?xml version="1.0" encoding="UTF-8" ?>'
237        '<config>'
238        '  <target name="unittest">'
239        '    <overlay name="unittest1"/>'
240        '  </target>'
241        '</config>'
242        )
243      test_config.flush()
244      with self.assertRaises(KeyError):
245        overlay.BindOverlay(
246            config_file=test_config.name,
247            target='unknown',
248            source_dir=self.source_dir)
249
250
251if __name__ == '__main__':
252  unittest.main()
253