1from __future__ import print_function
2
3# lldb test suite imports
4from lldbsuite.test.decorators import *
5from lldbsuite.test.lldbtest import TestBase
6
7# gdb-remote-specific imports
8import lldbgdbserverutils
9from gdbremote_testcase import GdbRemoteTestCaseBase
10
11import binascii
12import stat
13
14
15class TestGdbRemotePlatformFile(GdbRemoteTestCaseBase):
16
17    mydir = TestBase.compute_mydir(__file__)
18
19    @skipIfWindows
20    @add_test_categories(["llgs"])
21    def test_platform_file_rdonly(self):
22        self.vFile_test(read=True)
23
24    @skipIfWindows
25    @add_test_categories(["llgs"])
26    def test_platform_file_wronly(self):
27        self.vFile_test(write=True)
28
29    @skipIfWindows
30    @add_test_categories(["llgs"])
31    def test_platform_file_rdwr(self):
32        self.vFile_test(read=True, write=True)
33
34    @skipIfWindows
35    @add_test_categories(["llgs"])
36    def test_platform_file_wronly_append(self):
37        self.vFile_test(write=True, append=True)
38
39    @skipIfWindows
40    @add_test_categories(["llgs"])
41    def test_platform_file_rdwr_append(self):
42        self.vFile_test(read=True, write=True, append=True)
43
44    @skipIfWindows
45    @add_test_categories(["llgs"])
46    def test_platform_file_wronly_trunc(self):
47        self.vFile_test(write=True, trunc=True)
48
49    @skipIfWindows
50    @add_test_categories(["llgs"])
51    def test_platform_file_rdwr_trunc(self):
52        self.vFile_test(read=True, write=True, trunc=True)
53
54    @skipIfWindows
55    @add_test_categories(["llgs"])
56    def test_platform_file_wronly_creat(self):
57        self.vFile_test(write=True, creat=True)
58
59    @skipIfWindows
60    @add_test_categories(["llgs"])
61    def test_platform_file_wronly_creat_excl(self):
62        self.vFile_test(write=True, creat=True, excl=True)
63
64    @skipIfWindows
65    @add_test_categories(["llgs"])
66    def test_platform_file_wronly_fail(self):
67        server = self.connect_to_debug_monitor()
68        self.assertIsNotNone(server)
69
70        temp_path = self.getBuildArtifact("test")
71        self.assertFalse(os.path.exists(temp_path))
72
73        # attempt to open the file without O_CREAT
74        self.do_handshake()
75        self.test_sequence.add_log_lines(
76            ["read packet: $vFile:open:%s,1,0#00" % (
77                binascii.b2a_hex(temp_path.encode()).decode(),),
78             {"direction": "send",
79             "regex": r"^\$F-1,[0-9a-fA-F]+#[0-9a-fA-F]{2}$"}],
80            True)
81        self.expect_gdbremote_sequence()
82
83    @skipIfWindows
84    @add_test_categories(["llgs"])
85    def test_platform_file_wronly_creat_excl_fail(self):
86        server = self.connect_to_debug_monitor()
87        self.assertIsNotNone(server)
88
89        temp_file = self.getBuildArtifact("test")
90        with open(temp_file, "wb"):
91            pass
92
93        # attempt to open the file with O_CREAT|O_EXCL
94        self.do_handshake()
95        self.test_sequence.add_log_lines(
96            ["read packet: $vFile:open:%s,a01,0#00" % (
97                binascii.b2a_hex(temp_file.encode()).decode(),),
98             {"direction": "send",
99             "regex": r"^\$F-1,[0-9a-fA-F]+#[0-9a-fA-F]{2}$"}],
100            True)
101        self.expect_gdbremote_sequence()
102
103    @skipIfWindows
104    @add_test_categories(["llgs"])
105    def test_platform_file_size(self):
106        server = self.connect_to_debug_monitor()
107        self.assertIsNotNone(server)
108
109        temp_path = self.getBuildArtifact("test")
110        test_data = b"test data of some length"
111        with open(temp_path, "wb") as temp_file:
112            temp_file.write(test_data)
113
114        self.do_handshake()
115        self.test_sequence.add_log_lines(
116            ["read packet: $vFile:size:%s#00" % (
117                binascii.b2a_hex(temp_path.encode()).decode(),),
118             {"direction": "send",
119             "regex": r"^\$F([0-9a-fA-F]+)+#[0-9a-fA-F]{2}$",
120             "capture": {1: "size"}}],
121            True)
122        context = self.expect_gdbremote_sequence()
123        self.assertEqual(int(context["size"], 16), len(test_data))
124
125    @skipIfWindows
126    @add_test_categories(["llgs"])
127    def test_platform_file_mode(self):
128        server = self.connect_to_debug_monitor()
129        self.assertIsNotNone(server)
130
131        temp_path = self.getBuildArtifact("test")
132        test_mode = 0o751
133
134        with open(temp_path, "wb") as temp_file:
135            os.chmod(temp_file.fileno(), test_mode)
136
137        self.do_handshake()
138        self.test_sequence.add_log_lines(
139            ["read packet: $vFile:mode:%s#00" % (
140                binascii.b2a_hex(temp_path.encode()).decode(),),
141             {"direction": "send",
142             "regex": r"^\$F([0-9a-fA-F]+)+#[0-9a-fA-F]{2}$",
143             "capture": {1: "mode"}}],
144            True)
145        context = self.expect_gdbremote_sequence()
146        self.assertEqual(int(context["mode"], 16), test_mode)
147
148    @skipIfWindows
149    @add_test_categories(["llgs"])
150    def test_platform_file_exists(self):
151        server = self.connect_to_debug_monitor()
152        self.assertIsNotNone(server)
153
154        temp_path = self.getBuildArtifact("test")
155        with open(temp_path, "wb"):
156            pass
157
158        self.do_handshake()
159        self.test_sequence.add_log_lines(
160            ["read packet: $vFile:exists:%s#00" % (
161                binascii.b2a_hex(temp_path.encode()).decode(),),
162             "send packet: $F,1#00"],
163            True)
164        self.expect_gdbremote_sequence()
165
166    @skipIfWindows
167    @add_test_categories(["llgs"])
168    def test_platform_file_exists_not(self):
169        server = self.connect_to_debug_monitor()
170        self.assertIsNotNone(server)
171
172        test_path = self.getBuildArtifact("nonexist")
173        self.do_handshake()
174        self.test_sequence.add_log_lines(
175            ["read packet: $vFile:exists:%s#00" % (
176                binascii.b2a_hex(test_path.encode()).decode(),),
177             "send packet: $F,0#00"],
178            True)
179        self.expect_gdbremote_sequence()
180
181    def expect_error(self):
182        self.test_sequence.add_log_lines(
183            [{"direction": "send",
184             "regex": r"^\$F-1,[0-9a-fA-F]+#[0-9a-fA-F]{2}$"}],
185            True)
186        self.expect_gdbremote_sequence()
187
188    def vFile_test(self, read=False, write=False, append=False, trunc=False,
189                   creat=False, excl=False):
190        if read and write:
191            mode = 2
192        elif write:
193            mode = 1
194        else:  # read
195            mode = 0
196        if append:
197            mode |= 8
198        if creat:
199            mode |= 0x200
200        if trunc:
201            mode |= 0x400
202        if excl:
203            mode |= 0x800
204
205        old_umask = os.umask(0o22)
206        try:
207            server = self.connect_to_debug_monitor()
208        finally:
209            os.umask(old_umask)
210        self.assertIsNotNone(server)
211
212        # create a temporary file with some data
213        temp_path = self.getBuildArtifact("test")
214        test_data = 'some test data longer than 16 bytes\n'
215
216        if creat:
217            self.assertFalse(os.path.exists(temp_path))
218        else:
219            with open(temp_path, "wb") as temp_file:
220                temp_file.write(test_data.encode())
221
222        # open the file for reading
223        self.do_handshake()
224        self.test_sequence.add_log_lines(
225            ["read packet: $vFile:open:%s,%x,1a0#00" % (
226                binascii.b2a_hex(temp_path.encode()).decode(),
227                mode),
228             {"direction": "send",
229             "regex": r"^\$F([0-9a-fA-F]+)#[0-9a-fA-F]{2}$",
230             "capture": {1: "fd"}}],
231            True)
232
233        context = self.expect_gdbremote_sequence()
234        self.assertIsNotNone(context)
235        fd = int(context["fd"], 16)
236
237        # read data from the file
238        self.reset_test_sequence()
239        self.test_sequence.add_log_lines(
240            ["read packet: $vFile:pread:%x,11,10#00" % (fd,)],
241            True)
242        if read:
243            self.test_sequence.add_log_lines(
244                [{"direction": "send",
245                 "regex": r"^\$F([0-9a-fA-F]+);(.*)#[0-9a-fA-F]{2}$",
246                 "capture": {1: "size", 2: "data"}}],
247                True)
248            context = self.expect_gdbremote_sequence()
249            self.assertIsNotNone(context)
250            if trunc:
251                self.assertEqual(context["size"], "0")
252                self.assertEqual(context["data"], "")
253            else:
254                self.assertEqual(context["size"], "11")  # hex
255                self.assertEqual(context["data"], test_data[0x10:0x10 + 0x11])
256        else:
257            self.expect_error()
258
259        # another offset
260        if read and not trunc:
261            self.reset_test_sequence()
262            self.test_sequence.add_log_lines(
263                ["read packet: $vFile:pread:%x,6,3#00" % (fd,),
264                 {"direction": "send",
265                 "regex": r"^\$F([0-9a-fA-F]+);(.+)#[0-9a-fA-F]{2}$",
266                 "capture": {1: "size", 2: "data"}}],
267                True)
268            context = self.expect_gdbremote_sequence()
269            self.assertIsNotNone(context)
270            self.assertEqual(context["size"], "6")  # hex
271            self.assertEqual(context["data"], test_data[3:3 + 6])
272
273        # write data to the file
274        self.reset_test_sequence()
275        self.test_sequence.add_log_lines(
276            ["read packet: $vFile:pwrite:%x,6,somedata#00" % (fd,)],
277            True)
278        if write:
279            self.test_sequence.add_log_lines(
280                ["send packet: $F8#00"],
281                True)
282            self.expect_gdbremote_sequence()
283        else:
284            self.expect_error()
285
286        # close the file
287        self.reset_test_sequence()
288        self.test_sequence.add_log_lines(
289            ["read packet: $vFile:close:%x#00" % (fd,),
290             "send packet: $F0#00"],
291            True)
292        self.expect_gdbremote_sequence()
293
294        if write:
295            # check if the data was actually written
296            with open(temp_path, "rb") as temp_file:
297                if creat:
298                    self.assertEqual(os.fstat(temp_file.fileno()).st_mode & 0o7777,
299                                     0o640)
300                data = test_data.encode()
301                if trunc or creat:
302                    data = b"\0" * 6 + b"somedata"
303                elif append:
304                    data += b"somedata"
305                else:
306                    data = data[:6] + b"somedata" + data[6 + 8:]
307                self.assertEqual(temp_file.read(), data)
308