File: TestSwiftInterfaceNoDebugInfo.py

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (190 lines) | stat: -rw-r--r-- 8,293 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# TestSwiftInterfaceNoDebugInfo.py
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
#
# -----------------------------------------------------------------------------
"""
Test that we load and handle swift modules that only have textual
.swiftinterface files -- i.e. no associated .swiftmodule file -- and no debug
info. The module loader should generate the .swiftmodule for any
.swiftinterface it finds unless it is already in the module cache.
"""

import glob
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
import os
import os.path
import unittest2


class TestSwiftInterfaceNoDebugInfo(TestBase):
    @swiftTest
    def test_swift_interface(self):
        """Test that we load and handle modules that only have textual .swiftinterface files"""
        self.build()
        self.do_test()

    @swiftTest
    def test_swift_interface_fallback(self):
        """Test that we fall back to load from the .swiftinterface file if the .swiftmodule is invalid"""
        self.build()
        # Install invalid modules in the build directory first to check we
        # still fall back to the .swiftinterface.
        modules = ['AA.swiftmodule', 'BB.swiftmodule', 'CC.swiftmodule']
        for module in modules:
            open(self.getBuildArtifact(module), 'w').close()
        self.do_test()

    @swiftTest
    @skipUnlessPlatform(["macosx"])
    def test_prebuilt_cache_location(self):
        """Verify the prebuilt cache path is correct"""
        self.build()
        log = self.getBuildArtifact("types.log")
        self.runCmd('log enable lldb types -f "%s"' % log)

        # Set a breakpoint in and launch the main executable so we load the
        # ASTContext and log the prebuilt cache path
        lldbutil.run_to_source_breakpoint(
           self, "break here", lldb.SBFileSpec("main.swift"),
           exe_name=self.getBuildArtifact("main"))
        self.expect("expr 1")

        # Check the prebuilt cache path in the log output
        prefix = 'Using prebuilt Swift module cache path: '
        expected_suffix = os.path.join('macosx', 'prebuilt-modules')
        found = False

        import io
        with io.open(log, "r", encoding='utf-8') as logfile:
            for line in logfile:
                if prefix in line:
                    self.assertTrue(line.rstrip().endswith(os.path.sep + expected_suffix), 'unexpected prebuilt cache path: ' + line)
                    found = True
                    break
        self.assertTrue(found, 'prebuilt cache path log entry not found')

        # Check the host toolchain has a prebuilt cache in the same subdirectory of its swift resource directory
        prebuilt_path = os.path.join(self.get_toolchain(), 'usr', 'lib', 'swift', expected_suffix)
        self.assertTrue(len(os.listdir(prebuilt_path)) > 0)

    def get_toolchain(self):
        sdkroot = self.get_sdkroot()
        # The SDK root is expected to be wihin the Xcode.app/Contents
        # directory. Drop the last path component from the sdkroot until we get
        # up to that level.
        self.assertTrue('{0}Contents{0}'.format(os.path.sep) in sdkroot)
        contents = os.path.abspath(sdkroot)
        while os.path.split(contents)[1] != 'Contents':
            (contents, _) =  os.path.split(contents)
        # Construct the expected path to the default toolchain from there and
        # check it exists.
        toolchain = os.path.join(contents, 'Developer', 'Toolchains', 'XcodeDefault.xctoolchain')
        self.assertTrue(os.path.exists(toolchain), 'no default toolchain?')
        return toolchain

    def get_sdkroot(self):
        with open(self.getBuildArtifact("sdk-root.txt"), "r") as sdkroot:
            return sdkroot.read().rstrip()

    def do_test(self):
        # The custom swift module cache location
        swift_mod_cache = self.getBuildArtifact("MCP")

        # Clear the swift module cache (populated by the Makefile build)
        shutil.rmtree(swift_mod_cache)
        self.assertFalse(os.path.isdir(swift_mod_cache),
                         "module cache should not exist")

        # Update the settings to use the custom module cache location
        self.runCmd('settings set symbols.clang-modules-cache-path "%s"'
                    % swift_mod_cache)

        # Set a breakpoint in and launch the main executable
        lldbutil.run_to_source_breakpoint(
            self, "break here", lldb.SBFileSpec("main.swift"),
            exe_name=self.getBuildArtifact("main"),
            extra_images=['AA', 'BB', 'CC'])

        # Check we are able to access the public fields of variables whose
        # types are from the .swiftinterface-only dylibs
        var = self.frame().FindVariable("x")
        lldbutil.check_variable(self, var, False, typename="AA.MyPoint")

        child_y = var.GetChildMemberWithName("y") # MyPoint.y is public
        lldbutil.check_variable(self, child_y, False, value="0")

        # MyPoint.x isn't public, but LLDB can find it through type metadata.
        child_x = var.GetChildMemberWithName("x")
        self.assertTrue(child_x.IsValid())

        # Expression evaluation using types from the .swiftinterface only
        # dylibs should work too
        lldbutil.check_expression(
            self, self.frame(), "y.magnitudeSquared", "404", use_summary=False)
        lldbutil.check_expression(
            self, self.frame(), "MyPoint(x: 1, y: 2).magnitudeSquared", "5",
            use_summary=False)

        # Check the swift module cache was populated with the .swiftmodule
        # files of the loaded modules
        self.assertTrue(os.path.isdir(swift_mod_cache), "module cache exists")
        a_modules = glob.glob(os.path.join(swift_mod_cache, 'AA-*.swiftmodule'))
        b_modules = glob.glob(os.path.join(swift_mod_cache, 'BB-*.swiftmodule'))
        c_modules = glob.glob(os.path.join(swift_mod_cache, 'CC-*.swiftmodule'))
        self.assertEqual(len(a_modules), 1)
        self.assertEqual(len(b_modules), 1)
        self.assertEqual(len(c_modules), 0)

        # Update the timestamps of the modules to a time well in the past
        for file in a_modules + b_modules:
            make_old(file)

        # Re-import module A and B
        self.runCmd("expr import AA")
        self.runCmd("expr import BB")

        # Import C for the first time and check we can evaluate expressions
        # involving types from it
        self.runCmd("expr import CC")
        lldbutil.check_expression(
            self, self.frame(), "Baz.baz()", "23", use_summary=False)

        # Check we still have a single .swiftmodule in the cache for A and B
        # and that there is now one for C too
        a_modules = glob.glob(os.path.join(swift_mod_cache, 'AA-*.swiftmodule'))
        b_modules = glob.glob(os.path.join(swift_mod_cache, 'BB-*.swiftmodule'))
        c_modules = glob.glob(os.path.join(swift_mod_cache, 'CC-*.swiftmodule'))
        self.assertEqual(len(a_modules), 1,
                         "unexpected number of swiftmodules for A.swift")
        self.assertEqual(len(b_modules), 1,
                         "unexpected number of swiftmodules for B.swift")
        self.assertEqual(len(c_modules), 1,
                         "unexpected number of swiftmodules for C.swift")

        # Make sure the .swiftmodule files of A and B were re-used rather than
        # re-generated when they were re-imported
        for file in a_modules + b_modules:
            self.assertTrue(is_old(file),
                            "Swiftmodule file was regenerated rather than reused")


OLD_TIMESTAMP = 1390550700 # 2014-01-24T08:05:00+00:00

def make_old(file):
    """Sets the access and modified time of the given file to a time long past"""
    os.utime(file, (OLD_TIMESTAMP, OLD_TIMESTAMP))

def is_old(file):
    """Checks the modified time of the given file matches the timestamp set my make_old"""
    return os.stat(file).st_mtime == OLD_TIMESTAMP