github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/runtime/module_test.go (about) 1 // Copyright 2016 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package grumpy 16 17 import ( 18 "io/ioutil" 19 "os" 20 "testing" 21 ) 22 23 func TestImportModule(t *testing.T) { 24 f := NewRootFrame() 25 invalidModule := newObject(ObjectType) 26 foo := newTestModule("foo", "foo/__init__.py") 27 bar := newTestModule("foo.bar", "foo/bar/__init__.py") 28 baz := newTestModule("foo.bar.baz", "foo/bar/baz/__init__.py") 29 qux := newTestModule("foo.qux", "foo/qux/__init__.py") 30 fooCode := NewCode("<module>", "foo/__init__.py", nil, 0, func(*Frame, []*Object) (*Object, *BaseException) { return None, nil }) 31 barCode := NewCode("<module>", "foo/bar/__init__.py", nil, 0, func(*Frame, []*Object) (*Object, *BaseException) { return None, nil }) 32 bazCode := NewCode("<module>", "foo/bar/baz/__init__.py", nil, 0, func(*Frame, []*Object) (*Object, *BaseException) { return None, nil }) 33 quxCode := NewCode("<module>", "foo/qux/__init__.py", nil, 0, func(*Frame, []*Object) (*Object, *BaseException) { return None, nil }) 34 raisesCode := NewCode("<module", "raises.py", nil, 0, func(f *Frame, _ []*Object) (*Object, *BaseException) { 35 return nil, f.RaiseType(ValueErrorType, "uh oh") 36 }) 37 circularImported := false 38 circularCode := NewCode("<module>", "circular.py", nil, 0, func(f *Frame, _ []*Object) (*Object, *BaseException) { 39 if circularImported { 40 return nil, f.RaiseType(AssertionErrorType, "circular imported recursively") 41 } 42 circularImported = true 43 if _, raised := ImportModule(f, "circular"); raised != nil { 44 return nil, raised 45 } 46 return None, nil 47 }) 48 circularTestModule := newTestModule("circular", "circular.py").ToObject() 49 clearCode := NewCode("<module>", "clear.py", nil, 0, func(f *Frame, _ []*Object) (*Object, *BaseException) { 50 if _, raised := SysModules.DelItemString(f, "clear"); raised != nil { 51 return nil, raised 52 } 53 return None, nil 54 }) 55 // NOTE: This test progressively evolves sys.modules, checking after 56 // each test case that it's populated appropriately. 57 oldSysModules := SysModules 58 oldModuleRegistry := moduleRegistry 59 defer func() { 60 SysModules = oldSysModules 61 moduleRegistry = oldModuleRegistry 62 }() 63 SysModules = newStringDict(map[string]*Object{"invalid": invalidModule}) 64 moduleRegistry = map[string]*Code{ 65 "foo": fooCode, 66 "foo.bar": barCode, 67 "foo.bar.baz": bazCode, 68 "foo.qux": quxCode, 69 "raises": raisesCode, 70 "circular": circularCode, 71 "clear": clearCode, 72 } 73 cases := []struct { 74 name string 75 want *Object 76 wantExc *BaseException 77 wantSysModules *Dict 78 }{ 79 { 80 "noexist", 81 nil, 82 mustCreateException(ImportErrorType, "No module named noexist"), 83 newStringDict(map[string]*Object{"invalid": invalidModule}), 84 }, 85 { 86 "invalid", 87 NewTuple(invalidModule).ToObject(), 88 nil, 89 newStringDict(map[string]*Object{"invalid": invalidModule}), 90 }, 91 { 92 "raises", 93 nil, 94 mustCreateException(ValueErrorType, "uh oh"), 95 newStringDict(map[string]*Object{"invalid": invalidModule}), 96 }, 97 { 98 "foo", 99 NewTuple(foo.ToObject()).ToObject(), 100 nil, 101 newStringDict(map[string]*Object{ 102 "foo": foo.ToObject(), 103 "invalid": invalidModule, 104 }), 105 }, 106 { 107 "foo", 108 NewTuple(foo.ToObject()).ToObject(), 109 nil, 110 newStringDict(map[string]*Object{ 111 "foo": foo.ToObject(), 112 "invalid": invalidModule, 113 }), 114 }, 115 { 116 "foo.qux", 117 NewTuple(foo.ToObject(), qux.ToObject()).ToObject(), 118 nil, 119 newStringDict(map[string]*Object{ 120 "foo": foo.ToObject(), 121 "foo.qux": qux.ToObject(), 122 "invalid": invalidModule, 123 }), 124 }, 125 { 126 "foo.bar.baz", 127 NewTuple(foo.ToObject(), bar.ToObject(), baz.ToObject()).ToObject(), 128 nil, 129 newStringDict(map[string]*Object{ 130 "foo": foo.ToObject(), 131 "foo.bar": bar.ToObject(), 132 "foo.bar.baz": baz.ToObject(), 133 "foo.qux": qux.ToObject(), 134 "invalid": invalidModule, 135 }), 136 }, 137 { 138 "circular", 139 NewTuple(circularTestModule).ToObject(), 140 nil, 141 newStringDict(map[string]*Object{ 142 "circular": circularTestModule, 143 "foo": foo.ToObject(), 144 "foo.bar": bar.ToObject(), 145 "foo.bar.baz": baz.ToObject(), 146 "foo.qux": qux.ToObject(), 147 "invalid": invalidModule, 148 }), 149 }, 150 { 151 "clear", 152 nil, 153 mustCreateException(ImportErrorType, "Loaded module clear not found in sys.modules"), 154 newStringDict(map[string]*Object{ 155 "circular": circularTestModule, 156 "foo": foo.ToObject(), 157 "foo.bar": bar.ToObject(), 158 "foo.bar.baz": baz.ToObject(), 159 "foo.qux": qux.ToObject(), 160 "invalid": invalidModule, 161 }), 162 }, 163 } 164 for _, cas := range cases { 165 mods, raised := ImportModule(f, cas.name) 166 var got *Object 167 if raised == nil { 168 got = NewTuple(mods...).ToObject() 169 } 170 switch checkResult(got, cas.want, raised, cas.wantExc) { 171 case checkInvokeResultExceptionMismatch: 172 t.Errorf("ImportModule(%q) raised %v, want %v", cas.name, raised, cas.wantExc) 173 case checkInvokeResultReturnValueMismatch: 174 t.Errorf("ImportModule(%q) = %v, want %v", cas.name, got, cas.want) 175 } 176 ne := mustNotRaise(NE(f, SysModules.ToObject(), cas.wantSysModules.ToObject())) 177 b, raised := IsTrue(f, ne) 178 if raised != nil { 179 panic(raised) 180 } 181 if b { 182 msg := "ImportModule(%q): sys.modules = %v, want %v" 183 t.Errorf(msg, cas.name, SysModules, cas.wantSysModules) 184 } 185 } 186 } 187 func TestModuleGetNameAndFilename(t *testing.T) { 188 fun := wrapFuncForTest(func(f *Frame, m *Module) (*Tuple, *BaseException) { 189 name, raised := m.GetName(f) 190 if raised != nil { 191 return nil, raised 192 } 193 filename, raised := m.GetFilename(f) 194 if raised != nil { 195 return nil, raised 196 } 197 return newTestTuple(name, filename), nil 198 }) 199 cases := []invokeTestCase{ 200 {args: wrapArgs(newModule("foo", "foo.py")), want: newTestTuple("foo", "foo.py").ToObject()}, 201 {args: Args{mustNotRaise(ModuleType.Call(NewRootFrame(), wrapArgs("foo"), nil))}, wantExc: mustCreateException(SystemErrorType, "module filename missing")}, 202 {args: wrapArgs(&Module{Object: Object{typ: ModuleType, dict: NewDict()}}), wantExc: mustCreateException(SystemErrorType, "nameless module")}, 203 } 204 for _, cas := range cases { 205 if err := runInvokeTestCase(fun, &cas); err != "" { 206 t.Error(err) 207 } 208 } 209 } 210 211 func TestModuleInit(t *testing.T) { 212 fun := wrapFuncForTest(func(f *Frame, args ...*Object) (*Tuple, *BaseException) { 213 o, raised := ModuleType.Call(f, args, nil) 214 if raised != nil { 215 return nil, raised 216 } 217 name, raised := GetAttr(f, o, internedName, None) 218 if raised != nil { 219 return nil, raised 220 } 221 doc, raised := GetAttr(f, o, NewStr("__doc__"), None) 222 if raised != nil { 223 return nil, raised 224 } 225 return NewTuple(name, doc), nil 226 }) 227 cases := []invokeTestCase{ 228 {args: wrapArgs("foo"), want: newTestTuple("foo", None).ToObject()}, 229 {args: wrapArgs("foo", 123), want: newTestTuple("foo", 123).ToObject()}, 230 {args: wrapArgs(newObject(ObjectType)), wantExc: mustCreateException(TypeErrorType, `'__init__' requires a 'str' object but received a "object"`)}, 231 {wantExc: mustCreateException(TypeErrorType, "'__init__' requires 2 arguments")}, 232 } 233 for _, cas := range cases { 234 if err := runInvokeTestCase(fun, &cas); err != "" { 235 t.Error(err) 236 } 237 } 238 } 239 240 func TestModuleStrRepr(t *testing.T) { 241 cases := []invokeTestCase{ 242 {args: wrapArgs(newModule("foo", "<test>")), want: NewStr("<module 'foo' from '<test>'>").ToObject()}, 243 {args: wrapArgs(newModule("foo.bar.baz", "<test>")), want: NewStr("<module 'foo.bar.baz' from '<test>'>").ToObject()}, 244 {args: Args{mustNotRaise(ModuleType.Call(NewRootFrame(), wrapArgs("foo"), nil))}, want: NewStr("<module 'foo' (built-in)>").ToObject()}, 245 {args: wrapArgs(&Module{Object: Object{typ: ModuleType, dict: newTestDict("__file__", "foo.py")}}), want: NewStr("<module '?' from 'foo.py'>").ToObject()}, 246 } 247 for _, cas := range cases { 248 if err := runInvokeTestCase(wrapFuncForTest(ToStr), &cas); err != "" { 249 t.Error(err) 250 } 251 if err := runInvokeTestCase(wrapFuncForTest(Repr), &cas); err != "" { 252 t.Error(err) 253 } 254 } 255 } 256 257 func TestRunMain(t *testing.T) { 258 oldSysModules := SysModules 259 defer func() { 260 SysModules = oldSysModules 261 }() 262 cases := []struct { 263 code *Code 264 wantCode int 265 wantOutput string 266 }{ 267 {NewCode("<test>", "test.py", nil, 0, func(*Frame, []*Object) (*Object, *BaseException) { return None, nil }), 0, ""}, 268 {NewCode("<test>", "test.py", nil, 0, func(f *Frame, _ []*Object) (*Object, *BaseException) { 269 return nil, f.Raise(SystemExitType.ToObject(), None, nil) 270 }), 0, ""}, 271 {NewCode("<test>", "test.py", nil, 0, func(f *Frame, _ []*Object) (*Object, *BaseException) { return nil, f.RaiseType(TypeErrorType, "foo") }), 1, "TypeError: foo\n"}, 272 {NewCode("<test>", "test.py", nil, 0, func(f *Frame, _ []*Object) (*Object, *BaseException) { return nil, f.RaiseType(SystemExitType, "foo") }), 1, "foo\n"}, 273 {NewCode("<test>", "test.py", nil, 0, func(f *Frame, _ []*Object) (*Object, *BaseException) { 274 return nil, f.Raise(SystemExitType.ToObject(), NewInt(12).ToObject(), nil) 275 }), 12, ""}, 276 } 277 for _, cas := range cases { 278 SysModules = NewDict() 279 if gotCode, gotOutput, err := runMainAndCaptureStderr(cas.code); err != nil { 280 t.Errorf("runMainRedirectStderr() failed: %v", err) 281 } else if gotCode != cas.wantCode { 282 t.Errorf("RunMain() = %v, want %v", gotCode, cas.wantCode) 283 } else if gotOutput != cas.wantOutput { 284 t.Errorf("RunMain() output %q, want %q", gotOutput, cas.wantOutput) 285 } 286 } 287 } 288 289 func runMainAndCaptureStderr(code *Code) (int, string, error) { 290 oldStderr := Stderr 291 defer func() { 292 Stderr = oldStderr 293 }() 294 r, w, err := os.Pipe() 295 if err != nil { 296 return 0, "", err 297 } 298 Stderr = NewFileFromFD(w.Fd(), nil) 299 c := make(chan int) 300 go func() { 301 defer w.Close() 302 c <- RunMain(code) 303 }() 304 result := <-c 305 data, err := ioutil.ReadAll(r) 306 if err != nil { 307 return 0, "", err 308 } 309 return result, string(data), nil 310 } 311 312 var testModuleType *Type 313 314 func init() { 315 testModuleType, _ = newClass(NewRootFrame(), TypeType, "testModule", []*Type{ModuleType}, newStringDict(map[string]*Object{ 316 "__eq__": newBuiltinFunction("__eq__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 317 if raised := checkMethodArgs(f, "__eq__", args, ModuleType, ObjectType); raised != nil { 318 return nil, raised 319 } 320 if !args[1].isInstance(ModuleType) { 321 return NotImplemented, nil 322 } 323 m1, m2 := toModuleUnsafe(args[0]), toModuleUnsafe(args[1]) 324 name1, raised := m1.GetName(f) 325 if raised != nil { 326 return nil, raised 327 } 328 name2, raised := m2.GetName(f) 329 if raised != nil { 330 return nil, raised 331 } 332 if name1.Value() != name2.Value() { 333 return False.ToObject(), nil 334 } 335 file1, raised := m1.GetFilename(f) 336 if raised != nil { 337 return nil, raised 338 } 339 file2, raised := m2.GetFilename(f) 340 if raised != nil { 341 return nil, raised 342 } 343 return GetBool(file1.Value() == file2.Value()).ToObject(), nil 344 }).ToObject(), 345 "__ne__": newBuiltinFunction("__ne__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 346 if raised := checkMethodArgs(f, "__ne__", args, ModuleType, ObjectType); raised != nil { 347 return nil, raised 348 } 349 eq, raised := Eq(f, args[0], args[1]) 350 if raised != nil { 351 return nil, raised 352 } 353 isEq, raised := IsTrue(f, eq) 354 if raised != nil { 355 return nil, raised 356 } 357 return GetBool(!isEq).ToObject(), nil 358 }).ToObject(), 359 })) 360 } 361 362 func newTestModule(name, filename string) *Module { 363 return &Module{Object: Object{typ: testModuleType, dict: newTestDict("__name__", name, "__file__", filename)}} 364 }