Add compile:pin-components attribute to version tag of implementation template
[0compile.git] / tests / testcompile.py
blob289675674e9b4c0ac977660bfec7476f71c7243a
1 #!/usr/bin/env python
2 import sys, tempfile, os, shutil, tempfile, subprocess
3 from StringIO import StringIO
4 import unittest
5 from zeroinstall.injector import model, qdom, config
6 from zeroinstall.support import ro_rmtree, basedir
7 from zeroinstall.zerostore import Stores
9 stores = Stores()
11 mydir = os.path.abspath(os.path.dirname(__file__))
12 sys.path.insert(0, os.path.dirname(mydir))
13 import support
15 hello_uri = 'http://0install.net/tests/GNU-Hello.xml'
17 mydir = os.path.realpath(os.path.dirname(__file__))
19 hello_selections = os.path.join(mydir, 'selections.xml')
20 local_bad_version = os.path.join(mydir, 'bad-version.xml')
21 local_hello_path = os.path.join(mydir, 'hello2', 'hello2.xml')
22 local_cprog_command_path = os.path.join(mydir, 'cprog', 'cprog-command.xml')
23 local_cprog_path = os.path.join(mydir, 'cprog', 'cprog.xml')
24 local_pinned_path = os.path.join(mydir, 'pinned-version', 'pinned-version.xml')
25 top_build_deps = os.path.join(mydir, 'top-build-deps.xml')
27 compile_bin = os.path.join(mydir, '0compile-coverage')
28 assert os.path.exists(compile_bin)
30 if 'DISPLAY' in os.environ:
31 del os.environ['DISPLAY']
33 launch_command = [os.environ['0COMPILE_0LAUNCH']]
35 # Ensure it's cached now, to avoid extra output during the tests
36 if subprocess.call(launch_command + ['--source', '-c', '--download-only', hello_uri]):
37 raise Exception("Failed to download hello world test program")
39 def compile(*args, **kwargs):
40 run(*([sys.executable, compile_bin] + list(args)), **kwargs)
42 def run(*args, **kwargs):
43 if not isinstance(args[0], basestring):
44 args = args[0] + list(args[1:])
45 child = subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
46 got, unused = child.communicate()
47 code = child.wait()
48 if code != kwargs.get('expect_status', 0):
49 raise Exception("Exit status %d:\n%s" % (code, got))
51 expected = kwargs.get('expect', '')
52 if expected is not None: # pass None to explicily suppress check
53 if expected:
54 if expected.lower() not in got.lower():
55 raise Exception("Expected '%s', got '%s'" % (expected, got))
56 elif got:
57 raise Exception("Expected nothing, got '%s'" % got)
59 # Detect accidental network access
60 os.environ['http_proxy'] = 'localhost:1111'
62 for x in ['GNUPGHOME', 'XDG_CONFIG_HOME', 'XDG_CACHE_HOME', 'XDG_DATA_HOME']:
63 if x in os.environ:
64 del os.environ[x]
65 user_cache_dir = os.environ['XDG_CACHE_DIRS'] = basedir.xdg_cache_home
67 class TestCompile(unittest.TestCase):
68 def setUp(self):
69 os.chdir('/')
70 self.tmpdir = tempfile.mkdtemp(prefix = '0compile-test-')
71 self.hello_dir = os.path.join(self.tmpdir, 'hello')
73 os.environ['HOME'] = self.tmpdir
74 reload(basedir)
76 config_dir = basedir.save_config_path('0install.net', 'injector')
77 stream = open(os.path.join(config_dir, 'implementation-dirs'), 'w')
78 for x in stores.stores:
79 stream.write(x.dir + '\n')
80 stream.close()
82 stream = open(os.path.join(config_dir, 'global'), 'w')
83 stream.write('[global]\n'
84 'freshness = -1\n'
85 'help_with_testing = True\n'
86 'network_use = off-line\n')
87 stream.close()
89 def tearDown(self):
90 os.chdir(os.path.join(self.tmpdir, os.path.pardir))
91 ro_rmtree(self.tmpdir)
93 def testBadCommand(self):
94 compile('foo', expect = 'usage: 0compile', expect_status = 1)
95 compile('setup', hello_uri, self.tmpdir, expect = 'already exists', expect_status = 1)
96 os.chdir(self.tmpdir)
97 compile('setup', expect = 'Run 0compile from a directory containing', expect_status = 1)
98 compile('build', expect = 'Run 0compile from a directory containing', expect_status = 1)
99 compile('publish', expect = 'Run 0compile from a directory containing', expect_status = 1)
101 def testCompileNoDir(self):
102 os.chdir(self.tmpdir)
103 compile('setup', hello_uri, expect = 'Created directory')
104 os.chdir('GNU-Hello')
106 def testCompile(self):
107 compile('setup', hello_uri, self.hello_dir, expect = 'Created directory')
108 os.chdir(self.hello_dir)
110 compile('build', expect = 'Executing: "%s"' % os.path.join('$SRCDIR','configure'))
112 target_dir = 'gnu-hello-%s' % support.get_arch_name().lower()
113 archive_stem = 'gnu-hello-%s-1.3' % support.get_arch_name().lower()
114 assert os.path.isdir(target_dir), '%s not a directory' % target_dir
116 run(os.path.join(target_dir,'bin','hello'), expect = 'Hello, world!')
117 run(launch_command, os.path.join(target_dir,'0install', 'feed.xml'), expect = 'Hello, world!')
118 compile('publish', 'http://localhost/downloads', expect = "Now upload '%s.tar.bz2'" % archive_stem)
120 def testAutocompile(self):
121 compile('autocompile', hello_uri, expect = "site-packages/http/0install.net/tests__GNU-Hello.xml")
122 run(launch_command, hello_uri, expect = 'Hello, world!')
124 def testRecursive(self):
125 top = os.path.join(mydir, 'top.xml')
126 compile('autocompile', top, expect = "No dependencies need compiling... compile cprog itself...")
128 # Dependency was registered against its local path, since that was how we depended on it:
129 run(launch_command, os.path.join(mydir, 'cprog/cprog-command.xml'), expect = 'Hello from C')
131 # But the top-level feed was registered against its <feed-for>:
132 c = config.load_config()
133 i = c.iface_cache.get_interface('http://example.com/top.xml')
134 self.assertEquals(1, len(i.extra_feeds))
136 def testLocal(self):
137 compile('setup', local_hello_path, self.hello_dir, expect = 'Created directory')
138 os.chdir(self.hello_dir)
139 compile('build', expect = 'Executing: ls -l')
140 target_dir = 'hello2-any-any'
141 assert os.path.isdir(target_dir), '%s not a directory' % target_dir
143 run(launch_command, os.path.join(target_dir, '0install', 'feed.xml'), expect = 'ROX-Lib')
145 def testBadVersion(self):
146 compile('setup', local_bad_version, self.hello_dir, expect = 'Created directory')
147 os.chdir(self.hello_dir)
148 compile('build', expect = 'hello2-0.1 requires 0compile >= 300000', expect_status = 1)
150 def testCommand(self):
151 comp_dir = os.path.join(self.tmpdir, 'cprog-command')
152 compile('setup', local_cprog_command_path, comp_dir, expect = 'Created directory')
153 os.chdir(comp_dir)
154 compile('build', expect = 'Hello from C!')
155 target_dir = 'cprog-command-%s' % support.get_arch_name().lower()
156 binary_feed = os.path.join(target_dir, '0install', 'feed.xml')
157 run(launch_command, binary_feed, expect = 'Hello from C!')
158 s = open(binary_feed, 'r')
159 feed = model.ZeroInstallFeed(qdom.parse(s), binary_feed)
160 s.close()
161 impl, = feed.implementations.values()
162 assert impl.arch, "Missing arch on %s" % impl
163 self.assertEqual("Public Domain", str(impl.metadata['license']))
165 def testCopySrc(self):
166 comp_dir = os.path.join(self.tmpdir, 'cprog')
167 compile('setup', local_cprog_path, comp_dir, expect = 'Created directory')
168 os.chdir(comp_dir)
169 compile('diff', expect = "No local src directory to diff against", expect_status = 1)
170 compile('diff', 'foo', expect = 'usage', expect_status = 1)
171 compile('copy-src', 'foo', expect = 'usage', expect_status = 1)
172 compile('copy-src', expect = 'Copied as')
173 compile('copy-src', expect = "Directory '", expect_status = 1)
175 # 'src' exists, but no changes
176 compile('diff')
177 compile('--verbose', 'build', expect = 'Hello from C')
178 target_dir = 'cprog-%s' % support.get_arch_name().lower()
179 patch_file = os.path.join(target_dir, '0install', 'from-0.1.patch')
180 assert not os.path.exists(patch_file)
182 # 'src' contains a change
183 prog = file(os.path.join('src','main.c')).read()
184 prog = prog.replace('Hello', 'Goodbye')
185 stream = file(os.path.join('src','main.c'), 'w')
186 stream.write(prog)
187 stream.close()
188 compile('diff', expect = 'diff')
189 shutil.rmtree('build')
190 compile('build', expect = 'Goodbye from C')
191 assert os.path.exists(patch_file)
193 # Test dup-src's unlinking while we're here
194 compile('build', expect = 'Goodbye from C')
196 # 'src' contains an error
197 stream = file(os.path.join('src','main.c'), 'w')
198 stream.write('this is not valid C!')
199 stream.close()
200 shutil.rmtree('build')
201 compile('build', expect = 'Build failed', expect_status = 1)
202 assert os.path.exists(os.path.join('build', 'build-failure.log'))
204 # 'src' does not exist
205 shutil.rmtree('src')
206 shutil.rmtree('build')
207 compile('build', expect = 'Hello from C')
208 assert not os.path.exists(patch_file)
210 # Check we fixed the .pc files...
211 pc_data = open(os.path.join(target_dir, 'pkgconfig', 'cprog.pc')).read()
212 assert pc_data == "prefix=" + os.path.join("${pcfiledir}",os.path.pardir) + "\n", `pc_data`
214 # Check we removed the bad .la files...
215 assert not os.path.exists(os.path.join(target_dir, 'lib', 'bad.la')) # libtool - bad
216 assert os.path.exists(os.path.join(target_dir, 'lib', 'good.la')) # Ends in .la, but not a libtool archive
217 assert os.path.exists(os.path.join(target_dir, 'lib', 'nice.ok')) # Doesn't end in .la
219 def testInlcudeDeps(self):
220 compile('setup', hello_uri, self.hello_dir, expect = 'Created directory')
221 os.chdir(self.hello_dir)
222 os.unlink('0compile.properties')
223 compile('setup', hello_uri, '.')
224 compile('include-deps', expect = 'dependencies to')
225 compile('include-deps', expect = 'Copied 0 depend')
227 def testSetup(self):
228 compile('setup', hello_selections, self.hello_dir,
229 expect = 'Created directory')
230 compile('setup', hello_selections, self.hello_dir,
231 expect = "Directory '", expect_status = 1)
232 compile('setup', hello_selections, '.', 'foo',
233 expect = "usage", expect_status = 1)
234 os.chdir(self.hello_dir)
235 compile('setup', expect = "Selections are fixed", expect_status = 1)
237 def testReportBug(self):
238 broken_src = os.path.join(self.hello_dir, "broken.xml")
239 os.mkdir(self.hello_dir)
240 shutil.copy(local_hello_path, broken_src)
241 os.chdir(self.hello_dir)
242 compile('setup', broken_src, '.')
243 compile('build', expect = 'Build failed with exit code', expect_status = 1)
244 compile('report-bug', expect = "http://0install.net/api/report-bug")
246 env = support.BuildEnv()
247 os.unlink(os.path.join(env.metadir, "build-environment.xml"))
248 compile('report-bug', expect = "file+not+found")
250 def testBuildDeps(self):
251 compile('autocompile', top_build_deps, expect = "build-deps.xml 0.1 requires 3 <= version < 3", expect_status = 1)
253 def testPinVersions(self):
254 dest = os.path.join(self.tmpdir, 'pinned_version')
255 compile('setup', local_pinned_path, dest, expect = 'Created directory')
256 os.chdir(dest)
257 compile('build', expect=None)
258 env = support.BuildEnv()
259 python_version = subprocess.check_output(launch_command + [env.local_iface_file]).strip()
260 major, minor = map(int, python_version.split("."))
261 version_limit = "%d.%d" % (major, minor+1)
263 def check_feed(path):
265 Check that the feed has the restriction:
266 <version not-before="maj.minor" before="maj.minor+1"/>
268 from xml.dom import minidom
269 with open(path) as f:
270 dom = minidom.parse(f)
271 version_tags = dom.documentElement.getElementsByTagName('version')
272 assert len(version_tags) == 1, version_tags
273 version_tag = version_tags[0]
275 self.assertEquals(version_tag.getAttribute("not-before"), python_version)
276 self.assertEquals(version_tag.getAttribute("before"), version_limit)
278 check_feed(env.local_iface_file)
280 compile('publish', 'http://localhost/downloads', expect=None)
281 check_feed('pinned-version-0.1.xml')
283 suite = unittest.makeSuite(TestCompile)
284 if __name__ == '__main__':
285 unittest.main()