Added --command option
[0test.git] / runner.py
blob3851da9290b0f7f0b5687bdcabc1c7de9ba65c96
1 # Copyright (C) 2010, Thomas Leonard
2 # Visit http://0install.net for details.
4 import os, sys, logging
5 from zeroinstall.injector import policy, model, run, arch, requirements
6 from reporting import format_combo
8 class VersionRestriction(model.Restriction):
9 def __init__(self, version):
10 self.version = version
12 def meets_restriction(self, impl):
13 return impl.get_version() == self.version
15 def __repr__(self):
16 return "version = %s" % self.version
18 class TestingArchitecture(arch.Architecture):
19 use = frozenset([None, "testing"])
21 def __init__(self, child_arch):
22 arch.Architecture.__init__(self, child_arch.os_ranks, child_arch.machine_ranks)
23 self.child_arch = child_arch
25 def run_tests(config, tested_iface, sels, spec):
26 def _get_implementation_path(impl):
27 return impl.local_path or config.iface_cache.stores.lookup_any(impl.digests)
29 main_command = sels.commands[0] if sels.commands else None
31 root_impl = sels.selections[tested_iface.uri]
32 assert root_impl
34 if spec.test_wrapper:
35 tests_dir = None
36 # $1 is the main executable, or the root of the package if there isn't one
37 # We have to add the slash because otherwise 0launch interprets the path
38 # relative to itself...
39 if main_command and main_command.path:
40 test_main = "/" + main_command.path
41 else:
42 test_main = "/"
43 else:
44 if main_command is None:
45 print >>sys.stderr, "No <command> requested and no test command either!"
46 return "skipped"
48 test_main = None
50 if main_command.path is None:
51 tests_dir = _get_implementation_path(root_impl)
52 else:
53 main_abs = os.path.join(_get_implementation_path(root_impl), main_command.path)
54 if not os.path.exists(main_abs):
55 print >>sys.stderr, "Test executable does not exist:", main_abs
56 return "skipped"
58 tests_dir = os.path.dirname(main_abs)
60 child = os.fork()
61 if child:
62 # We are the parent
63 pid, status = os.waitpid(child, 0)
64 assert pid == child
65 print "Status:", hex(status)
66 if status == 0:
67 return "passed"
68 else:
69 return "failed"
70 else:
71 # We are the child
72 try:
73 try:
74 if spec.test_wrapper is None:
75 os.chdir(tests_dir)
76 run.execute_selections(sels, spec.test_args, main = test_main, wrapper = spec.test_wrapper)
77 os._exit(0)
78 except model.SafeException, ex:
79 try:
80 print >>sys.stderr, unicode(ex)
81 except:
82 print >>sys.stderr, repr(ex)
83 except:
84 import traceback
85 traceback.print_exc()
86 finally:
87 sys.stdout.flush()
88 sys.stderr.flush()
89 os._exit(1)
91 class Results:
92 def __init__(self, spec):
93 self.spec = spec
94 self.by_combo = {} # { set((uri, version)) : status }
95 self.by_status = { # status -> [ selections ]
96 'passed': [],
97 'skipped': [],
98 'failed': [],
101 def run_test_combinations(config, spec):
102 r = requirements.Requirements(spec.test_iface)
103 r.command = spec.command
104 ap = policy.Policy(config = config, requirements = r)
105 ap.target_arch = TestingArchitecture(ap.target_arch)
107 # Explore all combinations...
109 tested_iface = config.iface_cache.get_interface(spec.test_iface)
110 results = Results(spec)
111 for combo in spec.get_combos(spec.test_ifaces):
112 key = set()
113 restrictions = {}
114 selections = {}
115 for (uri, version) in combo.iteritems():
116 iface = config.iface_cache.get_interface(uri)
117 selections[iface] = version
119 if ',' in version:
120 not_before, before = [model.parse_version(v) if v != "" else None for v in version.split(',')]
121 if (not_before and before) and not_before >= before:
122 raise model.SafeException("Low version >= high version in %s!" % version)
123 restrictions[iface] = [model.VersionRangeRestriction(before, not_before)]
124 else:
125 model.parse_version(version) # Check format
126 restrictions[iface] = [VersionRestriction(version)]
127 key.add((uri, version))
129 ap.solver.extra_restrictions = restrictions
130 solve = ap.solve_with_downloads()
131 ap.handler.wait_for_blocker(solve)
132 if not ap.ready:
133 logging.info("Can't select combination %s: %s", combo, ap.solver.get_failure_reason())
134 result = 'skipped'
135 for uri, impl in ap.solver.selections.iteritems():
136 if impl is None:
137 selections[uri] = selections.get(uri, None) or '?'
138 else:
139 selections[uri] = impl.get_version()
140 else:
141 selections = {}
142 for iface, impl in ap.solver.selections.iteritems():
143 if impl:
144 version = impl.get_version()
145 else:
146 impl = None
147 selections[iface] = version
148 download = ap.download_uncached_implementations()
149 if download:
150 config.handler.wait_for_blocker(download)
152 tested_impl = ap.implementation[tested_iface]
154 print format_combo(selections)
156 result = run_tests(config, tested_iface, ap.solver.selections, spec)
158 results.by_status[result].append(selections)
159 results.by_combo[frozenset(key)] = (result, selections)
161 return results