this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "test-utils.h"
3
4#include <cmath>
5#include <cstdint>
6#include <cstring>
7#include <fstream>
8#include <functional>
9#include <iostream>
10#include <memory>
11#include <sstream>
12
13#include "attributedict.h"
14#include "builtins-module.h"
15#include "bytearray-builtins.h"
16#include "bytes-builtins.h"
17#include "compile-utils.h"
18#include "debugging.h"
19#include "exception-builtins.h"
20#include "frame.h"
21#include "handles.h"
22#include "ic.h"
23#include "int-builtins.h"
24#include "module-builtins.h"
25#include "modules.h"
26#include "os.h"
27#include "runtime.h"
28#include "set-builtins.h"
29#include "str-builtins.h"
30#include "sys-module.h"
31#include "thread.h"
32#include "type-builtins.h"
33#include "utils.h"
34
35namespace py {
36
37namespace testing {
38
39static RawObject initializeSysWithDefaults(Thread* thread) {
40 HandleScope scope(thread);
41 Runtime* runtime = thread->runtime();
42 unique_c_ptr<char> path(OS::executablePath());
43 Str executable(&scope, runtime->newStrFromCStr(path.get()));
44 List python_path(&scope, runtime->newList());
45 MutableTuple data(
46 &scope, runtime->newMutableTuple(static_cast<word>(SysFlag::kNumFlags)));
47 data.atPut(static_cast<word>(SysFlag::kDebug), SmallInt::fromWord(0));
48 data.atPut(static_cast<word>(SysFlag::kInspect), SmallInt::fromWord(0));
49 data.atPut(static_cast<word>(SysFlag::kInteractive), SmallInt::fromWord(0));
50 data.atPut(static_cast<word>(SysFlag::kOptimize), SmallInt::fromWord(0));
51 data.atPut(static_cast<word>(SysFlag::kDontWriteBytecode),
52 SmallInt::fromWord(0));
53 data.atPut(static_cast<word>(SysFlag::kNoUserSite), SmallInt::fromWord(1));
54 data.atPut(static_cast<word>(SysFlag::kNoSite), SmallInt::fromWord(1));
55 data.atPut(static_cast<word>(SysFlag::kIgnoreEnvironment),
56 SmallInt::fromWord(1));
57 data.atPut(static_cast<word>(SysFlag::kVerbose), SmallInt::fromWord(0));
58 data.atPut(static_cast<word>(SysFlag::kBytesWarning), SmallInt::fromWord(0));
59 data.atPut(static_cast<word>(SysFlag::kQuiet), SmallInt::fromWord(0));
60 data.atPut(static_cast<word>(SysFlag::kHashRandomization),
61 SmallInt::fromWord(1));
62 data.atPut(static_cast<word>(SysFlag::kIsolated), SmallInt::fromWord(0));
63 data.atPut(static_cast<word>(SysFlag::kDevMode), Bool::falseObj());
64 data.atPut(static_cast<word>(SysFlag::kUTF8Mode), SmallInt::fromWord(1));
65 static_assert(static_cast<word>(SysFlag::kNumFlags) == 15,
66 "unexpected flag count");
67 Tuple flags_data(&scope, data.becomeImmutable());
68 List warnoptions(&scope, runtime->newList());
69 return initializeSys(thread, executable, python_path, flags_data, warnoptions,
70 /*extend_python_path_with_stdlib=*/true);
71}
72
73bool useCppInterpreter() {
74 const char* pyro_cpp_interpreter = std::getenv("PYRO_CPP_INTERPRETER");
75 return pyro_cpp_interpreter != nullptr &&
76 ::strcmp(pyro_cpp_interpreter, "1") == 0;
77}
78
79Runtime* createTestRuntime() {
80 bool use_cpp_interpreter = useCppInterpreter();
81 word heap_size = 128 * kMiB;
82 Interpreter* interpreter =
83 use_cpp_interpreter ? createCppInterpreter() : createAsmInterpreter();
84 RandomState random_state = randomState();
85 Runtime* runtime =
86 new Runtime(heap_size, interpreter, random_state, StdioState::kBuffered);
87 Thread* thread = Thread::current();
88 CHECK(initializeSysWithDefaults(thread).isNoneType(),
89 "initializeSys() failed");
90 CHECK(runtime->initialize(thread).isNoneType(),
91 "Runtime::initialize() failed");
92 return runtime;
93}
94
95Value::Type Value::type() const { return type_; }
96
97bool Value::boolVal() const {
98 DCHECK(type() == Type::Bool, "expected bool");
99 return bool_;
100}
101
102word Value::intVal() const {
103 DCHECK(type() == Type::Int, "expected int");
104 return int_;
105}
106
107double Value::floatVal() const {
108 DCHECK(type() == Type::Float, "expected float");
109 return float_;
110}
111
112const char* Value::strVal() const {
113 DCHECK(type() == Type::Str, "expected str");
114 return str_;
115}
116
117template <typename T1, typename T2>
118::testing::AssertionResult badListValue(const char* actual_expr, word i,
119 const T1& actual, const T2& expected) {
120 return ::testing::AssertionFailure()
121 << "Value of: " << actual_expr << '[' << i << "]\n"
122 << " Actual: " << actual << '\n'
123 << "Expected: " << expected;
124}
125
126::testing::AssertionResult AssertPyListEqual(
127 const char* actual_expr, const char* /* expected_expr */,
128 const Object& actual, const std::vector<Value>& expected) {
129 Thread* thread = Thread::current();
130 Runtime* runtime = thread->runtime();
131
132 if (!actual.isList()) {
133 return ::testing::AssertionFailure()
134 << " Type of: " << actual_expr << "\n"
135 << " Actual: " << typeName(runtime, *actual) << "\n"
136 << "Expected: list";
137 }
138
139 HandleScope scope(thread);
140 List list(&scope, *actual);
141 if (static_cast<size_t>(list.numItems()) != expected.size()) {
142 return ::testing::AssertionFailure()
143 << "Length of: " << actual_expr << "\n"
144 << " Actual: " << list.numItems() << "\n"
145 << " Expected: " << expected.size();
146 }
147
148 for (size_t i = 0; i < expected.size(); i++) {
149 Object actual_item(&scope, list.at(i));
150 const Value& expected_item = expected[i];
151
152 auto bad_type = [&](const char* expected_type) {
153 return ::testing::AssertionFailure()
154 << " Type of: " << actual_expr << '[' << i << "]\n"
155 << " Actual: " << typeName(runtime, *actual_item) << '\n'
156 << "Expected: " << expected_type;
157 };
158
159 switch (expected_item.type()) {
160 case Value::Type::None: {
161 if (!actual_item.isNoneType()) return bad_type("RawNoneType");
162 break;
163 }
164
165 case Value::Type::Bool: {
166 if (!actual_item.isBool()) return bad_type("bool");
167 auto const actual_val = Bool::cast(*actual_item) == Bool::trueObj();
168 auto const expected_val = expected_item.boolVal();
169 if (actual_val != expected_val) {
170 return badListValue(actual_expr, i, actual_val ? "True" : "False",
171 expected_val ? "True" : "False");
172 }
173 break;
174 }
175
176 case Value::Type::Int: {
177 if (!actual_item.isInt()) return bad_type("int");
178 Int actual_val(&scope, *actual_item);
179 Int expected_val(&scope, runtime->newInt(expected_item.intVal()));
180 if (actual_val.compare(*expected_val) != 0) {
181 // TODO(bsimmers): Support multi-digit values when we can print them.
182 return badListValue(actual_expr, i, actual_val.digitAt(0),
183 expected_item.intVal());
184 }
185 break;
186 }
187
188 case Value::Type::Float: {
189 if (!actual_item.isFloat()) return bad_type("float");
190 auto const actual_val = Float::cast(*actual_item).value();
191 auto const expected_val = expected_item.floatVal();
192 if (std::abs(actual_val - expected_val) >= DBL_EPSILON) {
193 return badListValue(actual_expr, i, actual_val, expected_val);
194 }
195 break;
196 }
197
198 case Value::Type::Str: {
199 if (!actual_item.isStr()) return bad_type("str");
200 Str actual_val(&scope, *actual_item);
201 const char* expected_val = expected_item.strVal();
202 if (!actual_val.equalsCStr(expected_val)) {
203 return badListValue(actual_expr, i, actual_val, expected_val);
204 }
205 break;
206 }
207 }
208 }
209
210 return ::testing::AssertionSuccess();
211}
212
213RawObject callFunction(const Function& func, const Tuple& args) {
214 Thread* thread = Thread::current();
215 thread->stackPush(*func);
216 word args_length = args.length();
217 for (word i = 0; i < args_length; i++) {
218 thread->stackPush(args.at(i));
219 }
220 return Interpreter::call(thread, args_length);
221}
222
223bool tupleContains(const Tuple& object_array, const Object& key) {
224 for (word i = 0; i < object_array.length(); i++) {
225 if (object_array.at(i) == *key) {
226 return true;
227 }
228 }
229 return false;
230}
231
232bool listContains(const Object& list_obj, const Object& key) {
233 Thread* thread = Thread::current();
234 HandleScope scope(thread);
235 if (!thread->runtime()->isInstanceOfList(*list_obj)) {
236 return false;
237 }
238 List list(&scope, *list_obj);
239 for (word i = 0, num_items = list.numItems(); i < num_items; i++) {
240 if (list.at(i) == *key) {
241 return true;
242 }
243 }
244 return false;
245}
246
247bool setIncludes(Thread* thread, const SetBase& set, const Object& key) {
248 HandleScope scope(thread);
249 Object hash_obj(&scope, Interpreter::hash(thread, key));
250 CHECK(hash_obj.isSmallInt(), "key must be hashable");
251 word hash = SmallInt::cast(*hash_obj).value();
252 return setIncludes(thread, set, key, hash);
253}
254
255void setHashAndAdd(Thread* thread, const SetBase& set, const Object& value) {
256 HandleScope scope(thread);
257 Object hash_obj(&scope, Interpreter::hash(thread, value));
258 CHECK(hash_obj.isSmallInt(), "value must be hashable");
259 word hash = SmallInt::cast(*hash_obj).value();
260 setAdd(thread, set, value, hash);
261}
262
263static RawObject findModuleByCStr(Runtime* runtime, const char* name) {
264 HandleScope scope(Thread::current());
265 Object key(&scope, runtime->newStrFromCStr(name));
266 return runtime->findModule(key);
267}
268
269RawObject findMainModule(Runtime* runtime) {
270 return findModuleByCStr(runtime, "__main__");
271}
272
273RawObject mainModuleAt(Runtime* runtime, const char* name) {
274 return moduleAtByCStr(runtime, "__main__", name);
275}
276
277RawObject moduleAtByCStr(Runtime* runtime, const char* module_name,
278 const char* name) {
279 Thread* thread = Thread::current();
280 HandleScope scope(thread);
281 Object mod_obj(&scope, findModuleByCStr(runtime, module_name));
282 if (mod_obj.isNoneType()) {
283 return Error::notFound();
284 }
285 Module module(&scope, *mod_obj);
286 Object name_obj(&scope, Runtime::internStrFromCStr(thread, name));
287 return moduleAt(module, name_obj);
288}
289
290std::string typeName(Runtime* runtime, RawObject obj) {
291 if (obj.layoutId() == LayoutId::kError) return "Error";
292 RawStr name = Str::cast(Type::cast(runtime->typeOf(obj)).name());
293 word length = name.length();
294 std::string result(length, '\0');
295 name.copyTo(reinterpret_cast<byte*>(&result[0]), length);
296 return result;
297}
298
299RawObject typeValueCellAt(RawType type, RawObject name) {
300 RawObject result = NoneType::object();
301 if (!attributeValueCellAt(type, name, &result)) return Error::notFound();
302 return result;
303}
304
305static RawCode newCodeHelper(Thread* thread, View<byte> bytes,
306 const Tuple& consts, const Tuple& names,
307 Locals* locals, word flags) {
308 HandleScope scope(thread);
309 Runtime* runtime = thread->runtime();
310 word argcount = 0;
311 word posonlyargcount = 0;
312 word kwonlyargcount = 0;
313 word nlocals = 0;
314 word stacksize = 20;
315 Tuple varnames_tuple(&scope, runtime->emptyTuple());
316 if (locals != nullptr) {
317 argcount = locals->argcount;
318 posonlyargcount = locals->posonlyargcount;
319 kwonlyargcount = locals->kwonlyargcount;
320 nlocals = argcount + kwonlyargcount + locals->varcount;
321 if (locals->varargs) {
322 nlocals++;
323 flags |= Code::Flags::kVarargs;
324 }
325 if (locals->varkeyargs) {
326 nlocals++;
327 flags |= Code::Flags::kVarkeyargs;
328 }
329 MutableTuple varnames(&scope, runtime->newMutableTuple(nlocals));
330 word idx = 0;
331 for (word i = 0; i < locals->argcount; i++) {
332 varnames.atPut(idx++, runtime->newStrFromFmt("arg%w", i));
333 }
334 if (locals->varargs) {
335 varnames.atPut(idx++, runtime->newStrFromCStr("args"));
336 }
337 if (locals->varkeyargs) {
338 varnames.atPut(idx++, runtime->newStrFromCStr("kwargs"));
339 }
340 for (word i = 0; i < locals->varcount; i++) {
341 varnames.atPut(idx++, runtime->newStrFromFmt("var%w", i));
342 }
343 CHECK(idx == nlocals, "local count mismatch");
344 varnames_tuple = varnames.becomeImmutable();
345 }
346
347 Bytes code(&scope, runtime->newBytesWithAll(bytes));
348 Tuple empty_tuple(&scope, runtime->emptyTuple());
349 Object empty_string(&scope, Str::empty());
350 Object empty_bytes(&scope, Bytes::empty());
351 return Code::cast(runtime->newCode(/*argcount=*/argcount,
352 /*posonlyargcount=*/posonlyargcount,
353 /*kwonlyargcount=*/kwonlyargcount,
354 /*nlocals=*/nlocals,
355 /*stacksize=*/stacksize,
356 /*flags=*/flags, code, consts, names,
357 varnames_tuple,
358 /*freevars=*/empty_tuple,
359 /*cellvars=*/empty_tuple,
360 /*filename=*/empty_string,
361 /*name=*/empty_string,
362 /*firstlineno=*/0,
363 /*lnotab=*/empty_bytes));
364}
365
366RawCode newCodeWithBytesConstsNames(View<byte> bytes, const Tuple& consts,
367 const Tuple& names) {
368 Thread* thread = Thread::current();
369 word flags = Code::Flags::kOptimized | Code::Flags::kNewlocals;
370 return newCodeHelper(thread, bytes, consts, names, /*locals=*/nullptr, flags);
371}
372
373RawCode newCodeWithBytesConstsNamesFlags(View<byte> bytes, const Tuple& consts,
374 const Tuple& names, word flags) {
375 Thread* thread = Thread::current();
376 return newCodeHelper(thread, bytes, consts, names, /*locals=*/nullptr, flags);
377}
378
379RawCode newCodeWithBytesConstsNamesLocals(View<byte> bytes, const Tuple& consts,
380 const Tuple& names, Locals* locals) {
381 Thread* thread = Thread::current();
382 word flags = Code::Flags::kOptimized | Code::Flags::kNewlocals;
383 return newCodeHelper(thread, bytes, consts, names, locals, flags);
384}
385
386RawCode newCodeWithBytesConsts(View<byte> bytes, const Tuple& consts) {
387 Thread* thread = Thread::current();
388 HandleScope scope(thread);
389 Tuple names(&scope, thread->runtime()->emptyTuple());
390 return newCodeWithBytesConstsNames(bytes, consts, names);
391}
392
393RawCode newCodeWithBytes(View<byte> bytes) {
394 Thread* thread = Thread::current();
395 HandleScope scope(thread);
396 Tuple consts(&scope, thread->runtime()->emptyTuple());
397 return newCodeWithBytesConsts(bytes, consts);
398}
399
400RawFunction newEmptyFunction() {
401 Thread* thread = Thread::current();
402 HandleScope scope(thread);
403 Runtime* runtime = thread->runtime();
404 Code code(&scope, newCodeWithBytes(View<byte>(nullptr, 0)));
405 Object qualname(&scope, Str::empty());
406 Module main(&scope, findMainModule(runtime));
407 return Function::cast(
408 runtime->newFunctionWithCode(thread, qualname, code, main));
409}
410
411RawBytes newBytesFromCStr(Thread* thread, const char* str) {
412 return Bytes::cast(thread->runtime()->newBytesWithAll(
413 View<byte>(reinterpret_cast<const byte*>(str), std::strlen(str))));
414}
415
416RawBytearray newBytearrayFromCStr(Thread* thread, const char* str) {
417 HandleScope scope(thread);
418 Runtime* runtime = thread->runtime();
419 Bytearray result(&scope, runtime->newBytearray());
420 runtime->bytearrayExtend(
421 thread, result,
422 View<byte>(reinterpret_cast<const byte*>(str), std::strlen(str)));
423 return *result;
424}
425
426RawLargeInt newLargeIntWithDigits(View<uword> digits) {
427 Thread* thread = Thread::current();
428 HandleScope scope(thread);
429 LargeInt result(&scope, thread->runtime()->createLargeInt(digits.length()));
430 for (word i = 0, e = digits.length(); i < e; ++i) {
431 result.digitAtPut(i, digits.get(i));
432 }
433 return *result;
434}
435
436RawObject newMemoryView(View<byte> bytes, const char* format,
437 ReadOnly read_only) {
438 Thread* thread = Thread::current();
439 HandleScope scope(thread);
440 Runtime* runtime = thread->runtime();
441 Bytes bytes_obj(&scope, runtime->newBytesWithAll(bytes));
442 if (read_only == ReadOnly::ReadWrite) {
443 bytes_obj = runtime->mutableBytesFromBytes(thread, bytes_obj);
444 }
445 MemoryView result(&scope,
446 runtime->newMemoryView(thread, bytes_obj, bytes_obj,
447 bytes_obj.length(), read_only));
448 result.setFormat(Str::cast(runtime->newStrFromCStr(format)));
449 return *result;
450}
451
452RawTuple newTupleWithNone(word length) {
453 Thread* thread = Thread::current();
454 HandleScope scope(thread);
455 MutableTuple tuple(&scope, thread->runtime()->newMutableTuple(length));
456 tuple.fill(NoneType::object());
457 return Tuple::cast(tuple.becomeImmutable());
458}
459
460RawObject newWeakRefWithCallback(Runtime* runtime, Thread* thread,
461 const Object& referent,
462 const Object& callback) {
463 HandleScope scope(thread);
464 WeakRef ref(&scope, runtime->newWeakRef(thread, referent));
465 ref.setCallback(runtime->newBoundMethod(callback, ref));
466 return *ref;
467}
468
469RawObject setFromRange(word start, word stop) {
470 Thread* thread = Thread::current();
471 HandleScope scope(thread);
472 Set result(&scope, thread->runtime()->newSet());
473 Object value(&scope, NoneType::object());
474 Object hash_obj(&scope, NoneType::object());
475 for (word i = start; i < stop; i++) {
476 value = SmallInt::fromWord(i);
477 hash_obj = Interpreter::hash(thread, value);
478 if (hash_obj.isErrorException()) return *hash_obj;
479 word hash = SmallInt::cast(*hash_obj).value();
480 setAdd(thread, result, value, hash);
481 }
482 return *result;
483}
484
485RawObject runBuiltinImpl(BuiltinFunction function,
486 View<std::reference_wrapper<const Object>> args) {
487 Thread* thread = Thread::current();
488 HandleScope scope(thread);
489 // Push an empty function so we have one at the expected place in the stack.
490 word args_length = args.length();
491 Runtime* runtime = thread->runtime();
492 Tuple parameter_names(&scope, runtime->emptyTuple());
493 if (args_length > 0) {
494 MutableTuple parameter_names_mut(&scope,
495 runtime->newMutableTuple(args_length));
496 for (word i = 0; i < args_length; i++) {
497 parameter_names_mut.atPut(i, runtime->newStrFromFmt("arg%w", i));
498 }
499 parameter_names = parameter_names_mut.becomeImmutable();
500 }
501
502 Object name(&scope, Runtime::internStrFromCStr(thread, "<anonymous>"));
503 Code code(&scope, runtime->newBuiltinCode(args_length, /*posonlyargcount=*/0,
504 /*kwonlyargcount=*/0,
505 /*flags=*/0, function,
506 parameter_names, name));
507 Module main(&scope, findMainModule(runtime));
508 Function function_obj(&scope,
509 runtime->newFunctionWithCode(thread, name, code, main));
510
511 thread->stackPush(*function_obj);
512 for (word i = 0; i < args_length; i++) {
513 thread->stackPush(*args.get(i).get());
514 }
515 return Interpreter::call(thread, args_length);
516}
517
518RawObject runBuiltin(BuiltinFunction function) {
519 return runBuiltinImpl(function,
520 View<std::reference_wrapper<const Object>>{nullptr, 0});
521}
522
523RawObject runCode(const Code& code) {
524 Thread* thread = Thread::current();
525 HandleScope scope(thread);
526 Runtime* runtime = thread->runtime();
527 Module main(&scope, findMainModule(runtime));
528 Object qualname(&scope, Runtime::internStrFromCStr(thread, "<anonymous>"));
529 Function function(&scope,
530 runtime->newFunctionWithCode(thread, qualname, code, main));
531 return Interpreter::call0(thread, function);
532}
533
534RawObject runCodeNoBytecodeRewriting(const Code& code) {
535 Thread* thread = Thread::current();
536 HandleScope scope(thread);
537 Runtime* runtime = thread->runtime();
538 Module main(&scope, findMainModule(runtime));
539 Object qualname(&scope, Runtime::internStrFromCStr(thread, "<anonymous>"));
540 Bytes bytecode(&scope, code.code());
541 code.setCode(runtime->newBytes(0, 0));
542
543 Function function(&scope,
544 runtime->newFunctionWithCode(thread, qualname, code, main));
545 MutableBytes rewritten_bytecode(
546 &scope, runtime->newMutableBytesUninitialized(bytecode.length()));
547 rewritten_bytecode.replaceFromWithBytes(0, *bytecode, bytecode.length());
548 function.setRewrittenBytecode(*rewritten_bytecode);
549 return Interpreter::call0(thread, function);
550}
551
552RawObject runFromCStr(Runtime* runtime, const char* c_str) {
553 Thread* thread = Thread::current();
554 HandleScope scope(thread);
555 Object str(&scope, runtime->newStrFromCStr(c_str));
556 Object filename(&scope, runtime->newStrFromCStr("<test string>"));
557 Code code(&scope, compile(thread, str, filename, ID(exec), /*flags=*/0,
558 /*optimize=*/0));
559 Module main_module(&scope, findMainModule(runtime));
560 Object result(&scope, executeModule(thread, code, main_module));
561
562 // Barebones emulation of the top-level SystemExit handling, to allow for
563 // testing of handleSystemExit().
564 DCHECK(thread->isErrorValueOk(*result), "error/exception mismatch");
565 if (result.isError()) {
566 Type type(&scope, thread->pendingExceptionType());
567 if (type.builtinBase() == LayoutId::kSystemExit) handleSystemExit(thread);
568 }
569 return *result;
570}
571
572void addBuiltin(const char* name_cstr, BuiltinFunction function,
573 View<const char*> parameter_names, word flags) {
574 Thread* thread = Thread::current();
575 Runtime* runtime = thread->runtime();
576 HandleScope scope(thread);
577 Module main(&scope, findMainModule(runtime));
578 word num_parameters = parameter_names.length();
579 Object parameter_names_tuple(&scope, NoneType::object());
580 if (num_parameters > 0) {
581 MutableTuple parameter_names_mtuple(
582 &scope, runtime->newMutableTuple(num_parameters));
583 for (word i = 0; i < num_parameters; i++) {
584 parameter_names_mtuple.atPut(
585 i, Runtime::internStrFromCStr(thread, parameter_names.get(i)));
586 }
587 parameter_names_tuple = parameter_names_mtuple.becomeImmutable();
588 } else {
589 parameter_names_tuple = runtime->emptyTuple();
590 }
591 Object name(&scope, Runtime::internStrFromCStr(thread, name_cstr));
592 word argcount = num_parameters - ((flags & Code::Flags::kVarargs) != 0) -
593 ((flags & Code::Flags::kVarkeyargs) != 0);
594 Code code(&scope, runtime->newBuiltinCode(
595 /*argcount=*/argcount, /*posonlyargcount=*/0,
596 /*kwonlyargcount=*/0, flags, function,
597 /*parameter_names=*/parameter_names_tuple, name));
598 Function function_obj(&scope,
599 runtime->newFunctionWithCode(thread, name, code, main));
600 moduleAtPut(thread, main, name, function_obj);
601}
602
603// Equivalent to evaluating "list(range(start, stop))" in Python
604RawObject listFromRange(word start, word stop) {
605 Thread* thread = Thread::current();
606 HandleScope scope(thread);
607 List result(&scope, thread->runtime()->newList());
608 Object value(&scope, NoneType::object());
609 for (word i = start; i < stop; i++) {
610 value = SmallInt::fromWord(i);
611 thread->runtime()->listAdd(thread, result, value);
612 }
613 return *result;
614}
615
616RawObject icLookupAttr(RawMutableTuple caches, word index, LayoutId layout_id) {
617 word i = index * kIcPointersPerEntry;
618 bool is_found = false;
619 if (caches.at(i + kIcEntryValueOffset).isTuple()) {
620 return icLookupPolymorphic(caches, index, layout_id, &is_found);
621 }
622 return icLookupMonomorphic(caches, index, layout_id, &is_found);
623}
624
625RawObject icLookupBinaryOp(RawMutableTuple caches, word index,
626 LayoutId left_layout_id, LayoutId right_layout_id,
627 BinaryOpFlags* flags_out) {
628 word i = index * kIcPointersPerEntry;
629 if (caches.at(i + kIcEntryValueOffset).isTuple()) {
630 return icLookupBinOpPolymorphic(caches, index, left_layout_id,
631 right_layout_id, flags_out);
632 }
633 return icLookupBinOpMonomorphic(caches, index, left_layout_id,
634 right_layout_id, flags_out);
635}
636
637::testing::AssertionResult containsBytecode(const Function& function,
638 Bytecode bc) {
639 Thread* thread = Thread::current();
640 HandleScope scope(thread);
641 MutableBytes bytecode(&scope, function.rewrittenBytecode());
642 for (word i = 0, num_opcodes = rewrittenBytecodeLength(bytecode);
643 i < num_opcodes;) {
644 BytecodeOp bco = nextBytecodeOp(bytecode, &i);
645 if (bco.bc == bc) {
646 return ::testing::AssertionSuccess();
647 }
648 }
649 unique_c_ptr<char> name(Str::cast(function.name()).toCStr());
650 return ::testing::AssertionFailure()
651 << "opcode " << kBytecodeNames[static_cast<word>(bc)]
652 << " not found in '" << name.get() << "'";
653}
654
655::testing::AssertionResult isBytearrayEqualsBytes(const Object& result,
656 View<byte> expected) {
657 Thread* thread = Thread::current();
658 HandleScope scope(thread);
659 Runtime* runtime = thread->runtime();
660 if (result.isError()) {
661 if (result.isErrorException()) {
662 Type type(&scope, thread->pendingExceptionType());
663 unique_c_ptr<char> name(Str::cast(type.name()).toCStr());
664 return ::testing::AssertionFailure()
665 << "pending '" << name.get() << "' exception";
666 }
667 return ::testing::AssertionFailure() << "is an " << result;
668 }
669 if (!runtime->isInstanceOfBytearray(*result)) {
670 return ::testing::AssertionFailure()
671 << "is a '" << typeName(runtime, *result) << "'";
672 }
673 Bytearray result_array(&scope, *result);
674 Bytes result_bytes(&scope, bytearrayAsBytes(thread, result_array));
675 Bytes expected_bytes(&scope, runtime->newBytesWithAll(expected));
676 if (result_bytes.compare(*expected_bytes) != 0) {
677 return ::testing::AssertionFailure()
678 << "bytearray(" << result_bytes << ") is not equal to bytearray("
679 << expected_bytes << ")";
680 }
681 return ::testing::AssertionSuccess();
682}
683
684::testing::AssertionResult isBytearrayEqualsCStr(const Object& result,
685 const char* expected) {
686 return isBytearrayEqualsBytes(
687 result, View<byte>(reinterpret_cast<const byte*>(expected),
688 static_cast<word>(std::strlen(expected))));
689}
690
691::testing::AssertionResult isBytesEqualsBytes(const Object& result,
692 View<byte> expected) {
693 Thread* thread = Thread::current();
694 HandleScope scope(thread);
695 Runtime* runtime = thread->runtime();
696 if (result.isError()) {
697 if (result.isErrorException()) {
698 Type type(&scope, thread->pendingExceptionType());
699 unique_c_ptr<char> name(Str::cast(type.name()).toCStr());
700 return ::testing::AssertionFailure()
701 << "pending '" << name.get() << "' exception";
702 }
703 return ::testing::AssertionFailure() << "is an " << result;
704 }
705 if (!runtime->isInstanceOfBytes(*result)) {
706 return ::testing::AssertionFailure()
707 << "is a '" << typeName(runtime, *result) << "'";
708 }
709 Bytes result_bytes(&scope, bytesUnderlying(*result));
710 Bytes expected_bytes(&scope, runtime->newBytesWithAll(expected));
711 if (result_bytes.compare(*expected_bytes) != 0) {
712 return ::testing::AssertionFailure()
713 << result_bytes << " is not equal to " << expected_bytes;
714 }
715 return ::testing::AssertionSuccess();
716}
717
718::testing::AssertionResult isMutableBytesEqualsBytes(const Object& result,
719 View<byte> expected) {
720 if (!result.isError() && !result.isMutableBytes()) {
721 return ::testing::AssertionFailure()
722 << "is a '" << typeName(Thread::current()->runtime(), *result)
723 << "'";
724 }
725 return isBytesEqualsBytes(result, expected);
726}
727
728::testing::AssertionResult isBytesEqualsCStr(const Object& result,
729 const char* expected) {
730 return isBytesEqualsBytes(
731 result, View<byte>(reinterpret_cast<const byte*>(expected),
732 static_cast<word>(std::strlen(expected))));
733}
734
735::testing::AssertionResult isStrEquals(const Object& str1, const Object& str2) {
736 Thread* thread = Thread::current();
737 HandleScope scope(thread);
738 Runtime* runtime = thread->runtime();
739 if (!runtime->isInstanceOfStr(*str1)) {
740 return ::testing::AssertionFailure()
741 << "is a '" << typeName(runtime, *str1) << "'";
742 }
743 if (!runtime->isInstanceOfStr(*str2)) {
744 return ::testing::AssertionFailure()
745 << "is a '" << typeName(runtime, *str2) << "'";
746 }
747 Str s1(&scope, strUnderlying(*str1));
748 Str s2(&scope, strUnderlying(*str2));
749 if (!s1.equals(*s2)) {
750 unique_c_ptr<char> s2_ptr(s2.toCStr());
751 return ::testing::AssertionFailure()
752 << "is not equal to '" << s2_ptr.get() << "'";
753 }
754 return ::testing::AssertionSuccess();
755}
756
757::testing::AssertionResult isStrEqualsCStr(RawObject obj, const char* c_str) {
758 Thread* thread = Thread::current();
759 HandleScope scope(thread);
760 Object str_obj(&scope, obj);
761 Runtime* runtime = thread->runtime();
762 if (!runtime->isInstanceOfStr(*str_obj)) {
763 return ::testing::AssertionFailure()
764 << "is a '" << typeName(runtime, *str_obj) << "'";
765 }
766 Str str(&scope, strUnderlying(*str_obj));
767 if (!str.equalsCStr(c_str)) {
768 unique_c_ptr<char> str_ptr(str.toCStr());
769 return ::testing::AssertionFailure()
770 << "'" << str_ptr.get() << "' is not equal to '" << c_str << "'";
771 }
772 return ::testing::AssertionSuccess();
773}
774
775::testing::AssertionResult isSymbolIdEquals(SymbolId result,
776 SymbolId expected) {
777 if (result == expected) return ::testing::AssertionSuccess();
778 const char* result_name = result == SymbolId::kInvalid
779 ? "<Invalid>"
780 : Symbols::predefinedSymbolAt(result);
781 return ::testing::AssertionFailure()
782 << "Expected '" << Symbols::predefinedSymbolAt(expected)
783 << "', but got '" << result_name << "'";
784}
785
786::testing::AssertionResult isFloatEqualsDouble(RawObject obj, double expected) {
787 Thread* thread = Thread::current();
788 HandleScope scope(thread);
789 Runtime* runtime = thread->runtime();
790 if (obj.isError()) {
791 if (obj.isErrorException()) {
792 Type type(&scope, thread->pendingExceptionType());
793 Str type_name(&scope, type.name());
794 return ::testing::AssertionFailure()
795 << "pending " << type_name << " exception";
796 }
797 return ::testing::AssertionFailure() << "is an " << obj;
798 }
799 if (!runtime->isInstanceOfFloat(obj)) {
800 return ::testing::AssertionFailure()
801 << "is a '" << typeName(runtime, obj) << "'";
802 }
803 double value = floatUnderlying(obj).value();
804 // Test with memcmp instead of ==, so we can differentiate -0, and 0 or test
805 // for NaNs.
806 if (value != expected) {
807 return ::testing::AssertionFailure() << value << " is not " << expected;
808 }
809 return ::testing::AssertionSuccess();
810}
811
812::testing::AssertionResult isIntEqualsWord(RawObject obj, word value) {
813 Thread* thread = Thread::current();
814 HandleScope scope(thread);
815 Runtime* runtime = thread->runtime();
816 if (obj.isError()) {
817 if (obj.isErrorException()) {
818 Type type(&scope, thread->pendingExceptionType());
819 Str type_name(&scope, type.name());
820 return ::testing::AssertionFailure()
821 << "pending " << type_name << " exception";
822 }
823 return ::testing::AssertionFailure() << "is an " << obj;
824 }
825 if (!runtime->isInstanceOfInt(obj)) {
826 return ::testing::AssertionFailure()
827 << "is a '" << typeName(runtime, obj) << "'";
828 }
829 Object object(&scope, obj);
830 Int value_int(&scope, intUnderlying(*object));
831 if (value_int.numDigits() > 1 || value_int.asWord() != value) {
832 return ::testing::AssertionFailure()
833 << value_int << " is not equal to " << value;
834 }
835 return ::testing::AssertionSuccess();
836}
837
838::testing::AssertionResult isIntEqualsDigits(RawObject obj,
839 View<uword> digits) {
840 Thread* thread = Thread::current();
841 HandleScope scope(thread);
842 Runtime* runtime = thread->runtime();
843 if (obj.isError()) {
844 if (obj.isErrorException()) {
845 Type type(&scope, thread->pendingExceptionType());
846 Str type_name(&scope, type.name());
847 return ::testing::AssertionFailure()
848 << "pending " << type_name << " exception";
849 }
850 return ::testing::AssertionFailure() << "is an " << obj;
851 }
852 if (!runtime->isInstanceOfInt(obj)) {
853 return ::testing::AssertionFailure()
854 << "is a '" << typeName(runtime, obj) << "'";
855 }
856 Int expected(&scope, newLargeIntWithDigits(digits));
857 Object value_obj(&scope, obj);
858 Int value_int(&scope, intUnderlying(*value_obj));
859 if (expected.compare(*value_int) != 0) {
860 return ::testing::AssertionFailure()
861 << value_int << " is not equal to " << expected;
862 }
863 return ::testing::AssertionSuccess();
864}
865
866RawObject layoutCreateEmpty(Thread* thread) {
867 HandleScope scope(thread);
868 Runtime* runtime = thread->runtime();
869 LayoutId id = runtime->reserveLayoutId(thread);
870 Layout result(&scope, runtime->newLayout(id));
871 runtime->layoutSetTupleOverflow(*result);
872 runtime->layoutAtPut(id, *result);
873 return *result;
874}
875
876::testing::AssertionResult raised(RawObject return_value, LayoutId layout_id) {
877 return raisedWithStr(return_value, layout_id, nullptr);
878}
879
880::testing::AssertionResult raisedWithStr(RawObject return_value,
881 LayoutId layout_id,
882 const char* message) {
883 Thread* thread = Thread::current();
884 Runtime* runtime = thread->runtime();
885 HandleScope scope(thread);
886 Object return_value_obj(&scope, return_value);
887
888 if (!return_value_obj.isError()) {
889 Type type(&scope, runtime->typeOf(*return_value_obj));
890 Str name(&scope, type.name());
891 return ::testing::AssertionFailure()
892 << "call returned " << name << ", not Error";
893 }
894
895 if (!thread->hasPendingException()) {
896 return ::testing::AssertionFailure() << "no exception pending";
897 }
898
899 Type expected_type(&scope, runtime->typeAt(layout_id));
900 Type exception_type(&scope, thread->pendingExceptionType());
901 if (!typeIsSubclass(*exception_type, *expected_type)) {
902 Str expected_name(&scope, expected_type.name());
903 Str actual_name(&scope, exception_type.name());
904 return ::testing::AssertionFailure()
905 << "\npending exception has type:\n " << actual_name
906 << "\nexpected:\n " << expected_name << "\n";
907 }
908
909 if (message == nullptr) return ::testing::AssertionSuccess();
910
911 Object exc_value(&scope, thread->pendingExceptionValue());
912 if (!runtime->isInstanceOfStr(*exc_value)) {
913 if (runtime->isInstanceOfBaseException(*exc_value)) {
914 BaseException exc(&scope, *exc_value);
915 Tuple args(&scope, exc.args());
916 if (args.length() == 0) {
917 return ::testing::AssertionFailure()
918 << "pending exception args tuple is empty";
919 }
920 exc_value = args.at(0);
921 }
922
923 if (!runtime->isInstanceOfStr(*exc_value)) {
924 return ::testing::AssertionFailure()
925 << "pending exception value is not str";
926 }
927 }
928
929 Str exc_msg(&scope, *exc_value);
930 if (!exc_msg.equalsCStr(message)) {
931 return ::testing::AssertionFailure()
932 << "\npending exception value:\n '" << exc_msg
933 << "'\nexpected:\n '" << message << "'\n";
934 }
935
936 return ::testing::AssertionSuccess();
937}
938
939TemporaryDirectory::TemporaryDirectory() {
940 const char* tmpdir = std::getenv("TMPDIR");
941 if (tmpdir == nullptr) {
942 tmpdir = "/tmp";
943 }
944 const char format[] = "%s/PyroTest.XXXXXXXX";
945 word length = std::snprintf(nullptr, 0, format, tmpdir) + 1;
946 std::unique_ptr<char[]> buffer(new char[length]);
947 std::snprintf(buffer.get(), length, format, tmpdir);
948 char* result = ::mkdtemp(buffer.get());
949 CHECK(result != nullptr, "failed to create temporary directory");
950 path = buffer.get();
951 CHECK(!path.empty(), "must not be empty");
952 if (path.back() != '/') path += "/";
953}
954
955TemporaryDirectory::~TemporaryDirectory() {
956 std::string cleanup = "rm -rf " + path;
957 CHECK(system(cleanup.c_str()) == 0, "failed to cleanup temporary directory");
958}
959
960void writeFile(const std::string& path, const std::string& contents) {
961 CHECK(!path.empty() && path.front() == '/', "Should be an absolute path");
962 std::ofstream of(path);
963 CHECK(of.good(), "file creation failed");
964 of << contents;
965 CHECK(of.good(), "file write failed");
966 of.close();
967}
968
969} // namespace testing
970} // namespace py