2 # Copyright (C) 2007, Thomas Leonard
3 # See the README file for details, or visit http://0install.net.
4 import sys
, os
, shutil
, tempfile
, subprocess
, imp
5 from StringIO
import StringIO
8 from zeroinstall
.injector
import model
, qdom
, writer
9 from zeroinstall
.injector
.config
import load_config
10 from zeroinstall
.support
import basedir
, ro_rmtree
12 sys
.path
.insert(0, '..')
13 os
.environ
['http_proxy'] = 'localhost:1111' # Prevent accidental network access
16 import release
# (sets sys.path for 0repo)
19 mydir
= os
.path
.realpath(os
.path
.dirname(__file__
))
20 release_feed
= mydir
+ '/../0release.xml'
21 test_repo
= mydir
+ '/test-repo.tgz'
22 test_repo_actions
= mydir
+ '/test-repo-actions.tgz'
23 test_repo_c
= mydir
+ '/c-prog.tgz'
24 test_gpg
= mydir
+ '/gpg.tgz'
29 auto_approve_keys = True
30 help_with_testing = True
34 CUSTOM_REPO_CONFIG
= """
35 REPOSITORY_BASE_URL = "http://0install.net/tests/"
36 ARCHIVES_BASE_URL = "http://TESTING/releases"
38 def upload_archives(archives):
39 for dir_rel_url, files in paths.group_by_target_url_dir(archives):
40 target_dir = join('..', 'releases', 'archives') # hack: skip dir_rel_url
41 if not os.path.isdir(target_dir):
42 os.makedirs(target_dir)
43 subprocess.check_call(["cp"] + files + [target_dir])
45 def check_new_impl(impl):
48 def get_archive_rel_url(archive_basename, impl):
49 return "{version}/{archive}".format(
50 version = impl.get_version(),
51 archive = archive_basename)
54 def call_with_output_suppressed(cmd
, stdin
, expect_failure
= False, **kwargs
):
55 #cmd = [cmd[0], '-v'] + cmd[1:]
57 child
= subprocess
.Popen(cmd
, stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
, **kwargs
)
59 child
= subprocess
.Popen(cmd
, stdout
= subprocess
.PIPE
, **kwargs
)
60 stdout
, stderr
= child
.communicate(stdin
)
61 if (child
.returncode
!= 0) == expect_failure
:
64 raise Exception("Return code %d from %s\nstdout: %s\nstderr: %s" % (child
.returncode
, cmd
, stdout
, stderr
))
66 def make_releases_dir(src_feed
= '../hello/HelloWorld.xml', auto_upload
= False):
68 call_with_output_suppressed(['0release', src_feed
], None)
69 assert os
.path
.isfile('make-release')
71 lines
= file('make-release').readlines()
72 lines
[lines
.index('ARCHIVE_DIR_PUBLIC_URL=\n')] = 'ARCHIVE_DIR_PUBLIC_URL=http://TESTING/releases/\\$RELEASE_VERSION\n'
74 # Force us to test against this version of 0release
75 for i
, line
in enumerate(lines
):
76 if line
.startswith('exec 0launch http://0install.net/2007/interfaces/0release.xml --release'):
77 lines
[i
] = '0release --release ' + line
.split('--release ', 1)[1]
84 lines
[lines
.index('ARCHIVE_UPLOAD_COMMAND=\n')] = 'ARCHIVE_UPLOAD_COMMAND=\'cp "$@" ../archives/\'\n'
86 s
= file('make-release', 'w')
87 s
.write(''.join(lines
))
90 class TestRelease(unittest
.TestCase
):
92 self
.tmp
= tempfile
.mkdtemp(prefix
= '0release-')
94 support
.check_call(['tar', 'xzf', test_gpg
])
96 os
.environ
['GNUPGHOME'] = self
.tmp
+ '/gpg'
97 os
.chmod(os
.environ
['GNUPGHOME'], 0700)
99 config_dir
= os
.path
.join(self
.tmp
, 'config')
100 injector_config
= os
.path
.join(config_dir
, '0install.net', 'injector')
101 os
.makedirs(injector_config
)
102 s
= open(os
.path
.join(injector_config
, 'global'), 'w')
106 if 'ZEROINSTALL_PORTABLE_BASE' in os
.environ
:
107 del os
.environ
['ZEROINSTALL_PORTABLE_BASE']
108 os
.environ
['XDG_CONFIG_HOME'] = config_dir
110 assert basedir
.xdg_config_home
== config_dir
116 def testSimple(self
):
117 support
.check_call(['tar', 'xzf', test_repo
])
120 call_with_output_suppressed(['./make-release', '-k', 'Testing'], '\nP\n\n')
122 call_with_output_suppressed(['./make-release', '-k', 'Testing'], '\nP\nY\n\n')
124 assert 'Prints "Hello World"' in file('0.1/changelog-0.1').read()
125 assert 'Prints "Hello World"' not in file('0.2/changelog-0.2').read()
127 def testUncommitted(self
):
128 support
.check_call(['tar', 'xzf', test_repo_actions
])
131 unused
, stderr
= call_with_output_suppressed(['./make-release', '-k', 'Testing'], None,
132 expect_failure
= True, stderr
= subprocess
.PIPE
)
133 assert "Uncommitted changes!" in stderr
135 def testActions(self
):
136 support
.check_call(['tar', 'xzf', test_repo_actions
])
138 support
.check_call(['git', 'commit', '-a', '-m', 'Added release instructions'], stdout
= subprocess
.PIPE
)
142 assert "version = '0.2'\n" not in file('../hello/hello.py').read()
144 child
= subprocess
.Popen(['./make-release', '-k', 'Testing'], stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
)
145 unused
, unused
= child
.communicate('\nP\n\n')
146 assert child
.returncode
== 0
148 assert "version = '0.2'\n" in file('../hello/hello.py').read()
150 def testBinaryRelease(self
):
151 support
.check_call(['tar', 'xzf', test_repo_c
])
152 make_releases_dir(src_feed
= '../c-prog/c-prog.xml', auto_upload
= True)
154 call_with_output_suppressed(['./make-release', '-k', 'Testing', '--builders=host'], '\nP\n\n')
156 feed
= self
.get_public_feed('HelloWorld-in-C.xml', 'c-prog.xml')
158 assert len(feed
.implementations
) == 2
159 src_impl
, = [x
for x
in feed
.implementations
.values() if x
.arch
== '*-src']
160 host_impl
, = [x
for x
in feed
.implementations
.values() if x
.arch
!= '*-src']
162 assert src_impl
.main
== None
163 assert host_impl
.main
== 'hello'
165 archives
= os
.listdir('archives')
166 assert os
.path
.basename(src_impl
.download_sources
[0].url
) in archives
, src_impl
.download_sources
[0].url
168 host_download
= host_impl
.download_sources
[0]
169 self
.assertEqual('http://TESTING/releases/1.1/helloworld-in-c-linux-x86_64-1.1.tar.bz2',
171 host_archive
= os
.path
.basename(host_download
.url
)
172 assert host_archive
in archives
173 support
.check_call(['tar', 'xjf', os
.path
.join('archives', host_archive
)])
174 c
= subprocess
.Popen(['0launch', os
.path
.join(host_download
.extract
, '0install', 'feed.xml')], stdout
= subprocess
.PIPE
)
175 output
, _
= c
.communicate()
177 self
.assertEquals("Hello from C! (version 1.1)\n", output
)
179 def get_public_feed(self
, name
, uri_basename
):
180 with
open(name
, 'rb') as stream
:
181 return model
.ZeroInstallFeed(qdom
.parse(stream
))
186 old_stdout
= sys
.stdout
187 sys
.stdout
= StringIO()
189 sys
.stdin
= StringIO('\n') # (simulate a press of Return if needed)
190 repo
.cmd
.main(['0repo'] + args
)
191 return sys
.stdout
.getvalue()
194 sys
.stdout
= old_stdout
196 class TestRepoRelease(TestRelease
):
198 TestRelease
.setUp(self
)
200 # Let GPG initialise (it's a bit verbose)
201 child
= subprocess
.Popen(['gpg', '-q', '--list-secret-keys'], stdout
= subprocess
.PIPE
, stderr
= subprocess
.PIPE
)
202 unused
, unused
= child
.communicate()
205 run_repo(['create', 'my-repo', 'Testing <testing@example.com>'])
208 if '0repo-config' in sys
.modules
:
209 del sys
.modules
['0repo-config']
211 with
open('0repo-config.py', 'at') as stream
:
212 stream
.write(CUSTOM_REPO_CONFIG
)
213 run_repo(['register'])
216 def get_public_feed(self
, name
, uri_basename
):
217 with
open(os
.path
.join(self
.tmp
, 'my-repo', 'public', uri_basename
), 'rb') as stream
:
218 return model
.ZeroInstallFeed(qdom
.parse(stream
))
220 unittest
.makeSuite(TestRelease
)
221 unittest
.makeSuite(TestRepoRelease
)
222 if __name__
== '__main__':