vq / vlib / v / tests / interfaces / interface_test.v
550 lines · 466 sloc · 10.24 KB · 0fb301386f4f9a1ba175469390a138709b8ffe81
Raw
1struct Dog {
2 breed string
3}
4
5struct Cat {
6mut:
7 breed string
8}
9
10fn new_cat(breed string) Cat {
11 return Cat{breed}
12}
13
14fn (c &Cat) name() string {
15 if c.breed != '' {
16 assert c.breed == 'Persian'
17 }
18 return 'Cat'
19}
20
21fn (c &Cat) speak(s string) {
22 if c.breed != '' {
23 assert c.breed == 'Persian'
24 }
25 assert s == 'Hi !'
26 println('meow')
27}
28
29fn (c Cat) name_detailed(pet_name string) string {
30 return '${pet_name} the ${typeof(c).name}, breed:${c.breed}'
31}
32
33fn (mut c Cat) set_breed(new string) {
34 c.breed = new
35}
36
37// utility function to override default conversion to string, as a sample
38fn (c Cat) str() string {
39 return 'Custom string conversion for Cat: ${c.breed}'
40}
41
42fn (d Dog) speak(s string) {
43 assert s == 'Hi !'
44 println('woof')
45}
46
47fn (d Dog) name() string {
48 assert d.breed == 'Labrador Retriever'
49 return 'Dog'
50}
51
52fn (d Dog) name_detailed(pet_name string) string {
53 return '${pet_name} the ${typeof(d).name}, breed:${d.breed}'
54}
55
56fn (mut d Dog) set_breed(new string) {
57 println('Nah')
58}
59
60// do not add to Dog the utility function 'str', so the default one will be used, as a sample
61
62struct Bird {
63mut:
64 breed string
65}
66
67fn (b Bird) speak(s string) {
68 println('tweet')
69}
70
71fn (b Bird) name() string {
72 return b.breed
73}
74
75fn (b Bird) name_detailed(pet_name string) string {
76 return '${pet_name} the ${typeof(b).name}, breed:${b.breed}'
77}
78
79fn (mut b Bird) set_breed(new string) {
80 println('canary')
81 b.breed = new
82}
83
84// do not add to Bird the utility function 'str', so the default one will be used, as a sample
85
86fn is_dog_or_cat(a Animal) bool {
87 is_dog := a is Dog
88 is_cat := a is Cat
89 println('Animal is Dog or Cat: is a Dog: ${is_dog}, is a Cat: ${is_cat}')
90 // return is_dog || is_cat
91 // shorter syntax
92 is_dog_or_cat := if a is Dog || a is Cat { true } else { false }
93 println('Animal is Dog or Cat: ${is_dog_or_cat}')
94 return is_dog_or_cat
95}
96
97fn is_dog_or_cat_or_bird(a Animal) bool {
98 ret := a is Dog || a is Cat || a is Bird
99 println('Animal is Dog or Cat or Bird: ${ret}')
100 return ret
101}
102
103fn perform_speak(a Animal) {
104 println('---- ${@FN}, given Animal: ${a} ----')
105 a.speak('Hi !')
106 assert true
107 name := a.name()
108 assert name == 'Dog' || name == 'Cat'
109 if a is Dog {
110 assert name == 'Dog'
111 assert a.breed == 'Labrador Retriever' // test smart casting
112 println(a.breed)
113 }
114 println(a.name())
115 println('Got animal of type: ${typeof(a).name}') // TODO: get implementation type (if possible)
116 assert a is Dog || a is Cat
117 assert is_dog_or_cat(a)
118}
119
120fn perform_speak_on_ptr(a &Animal) {
121 println('---- ${@FN}, given &Animal: ${a} ----')
122 a.speak('Hi !')
123 assert true
124 name := a.name()
125 assert name == 'Dog' || name == 'Cat'
126 if a is Dog {
127 assert name == 'Dog'
128 }
129 println(a.name())
130 println('Got animal of type: ${typeof(a).name}') // TODO: get implementation type (if possible)
131 assert a is Dog || a is Cat
132 assert is_dog_or_cat(a)
133}
134
135fn test_perform_speak() {
136 println('---- ${@FN} ----')
137 dog := Dog{
138 breed: 'Labrador Retriever'
139 }
140 perform_speak(dog)
141 perform_speak_on_ptr(dog)
142 cat := Cat{
143 breed: 'Persian'
144 }
145 perform_speak(cat)
146 perform_speak(Cat{
147 breed: 'Persian'
148 })
149 perform_speak(new_cat('Persian'))
150 perform_speak_on_ptr(cat)
151 perform_speak_on_ptr(Cat{
152 breed: 'Persian'
153 })
154 perform_speak_on_ptr(new_cat('Persian'))
155 handle_animals([dog, cat])
156}
157
158fn change_animal_breed(mut a Animal, new string) {
159 a.set_breed(new)
160}
161
162fn test_interface_ptr_modification() {
163 println('---- ${@FN} ----')
164 mut cat := Cat{
165 breed: 'Persian'
166 }
167 change_animal_breed(mut cat, 'Siamese')
168 assert cat.breed == 'Siamese'
169}
170
171fn perform_name_detailed(a Animal) {
172 name_full := a.name_detailed('MyPet')
173 println(name_full)
174 assert name_full.starts_with('MyPet the Dog') || name_full.starts_with('MyPet the Cat')
175}
176
177fn test_perform_name_detailed() {
178 println('---- ${@FN} ----')
179 dog := Dog{
180 breed: 'Labrador Retriever'
181 }
182 println('Test on Dog: ${dog} ...') // using default conversion to string
183 perform_name_detailed(dog)
184 cat := Cat{}
185 println('Test on empty Cat: ${cat} ...')
186 perform_speak(cat)
187 println('Test on a Persian Cat: ...')
188 perform_speak(Cat{
189 breed: 'Persian'
190 })
191 cat_persian2 := Cat{
192 breed: 'Persian'
193 }
194 println('Test on another Persian Cat: "${cat_persian2}" ...')
195 perform_speak(cat_persian2)
196 cat_persian2_str := cat_persian2.str()
197 println("Persian Cat 2: '${cat_persian2_str}' ...")
198 assert cat_persian2_str == 'Custom string conversion for Cat: Persian'
199 println('Test (dummy/empty) on array of animals ...')
200 handle_animals([dog, cat])
201 handle_animals_mutable([dog, cat])
202 assert true
203}
204
205fn handle_animals(a []Animal) {
206}
207
208fn handle_animals_mutable(a []Animal) {
209}
210
211interface Register {
212 register()
213}
214
215struct RegTest {
216 a int
217}
218
219fn (f RegTest) register() {
220}
221
222fn handle_reg(r Register) {
223 r.register()
224}
225
226fn test_register() {
227 println('---- ${@FN} ----')
228 f := RegTest{}
229 f.register()
230 handle_reg(f)
231}
232
233interface Speaker2 {
234 name() string
235 speak()
236 return_speaker() Speaker2
237mut:
238 return_speaker2() ?Speaker2
239}
240
241struct Boss {
242mut:
243 name string
244}
245
246fn (b Boss) name() string {
247 return b.name
248}
249
250fn (b Boss) speak() {
251 println("i'm ${b.name}")
252}
253
254fn (b &Boss) return_speaker() Speaker2 {
255 return b
256}
257
258fn (mut b Boss) return_speaker2() ?Speaker2 {
259 if b.name == 'richard' {
260 return none
261 }
262 b.name = 'boss'
263 return b
264}
265
266fn return_speaker2(mut sp Speaker2) Speaker2 {
267 s := sp.return_speaker()
268 s2 := sp.return_speaker2() or { return *sp }
269 s.speak()
270 s2.speak()
271 return s2
272}
273
274fn test_interface_returning_interface() {
275 mut b := Boss{'bob'}
276 assert b.name == 'bob'
277 s2 := return_speaker2(mut b)
278 if s2 is Boss {
279 assert s2.name == 'boss'
280 }
281}
282
283struct Foo {
284 animal Animal
285 animals []Animal
286}
287
288interface Animal {
289 name() string
290 name_detailed(pet_name string) string
291 speak(s string)
292mut:
293 set_breed(s string)
294}
295
296fn test_interface_array() {
297 println('---- ${@FN} ----')
298 println('Test on array of animals ...')
299 mut animals := []Animal{}
300 animals = [Cat{}, Dog{
301 breed: 'Labrador Retriever'
302 }]
303 assert true
304 animals << Cat{}
305 assert true
306 animals << Bird{}
307 assert true
308 // println('Animals array contains: ${animals.str()}') // explicit call to 'str' function
309 println('Animals array contains: ${animals}') // implicit call to 'str' function
310 assert animals.len == 4
311}
312
313fn test_interface_ptr_array() {
314 println('---- ${@FN} ----')
315 mut animals := []&Animal{}
316 animals = [Cat{}, Dog{
317 breed: 'Labrador Retriever'
318 }]
319 assert true
320 animals << Cat{}
321 assert true
322 animals << Bird{}
323 assert true
324 // println('Animals array contains: ${animals.str()}') // explicit call to 'str' function
325 println('Animals array contains: ${animals}') // implicit call to 'str' function
326 assert animals.len == 4
327}
328
329fn test_is() {
330 println('---- ${@FN} ----')
331 dog := Dog{}
332 assert is_dog_int(dog) == 1
333}
334
335fn is_dog_int(a Animal) int {
336 if a is Dog {
337 return 1
338 } else {
339 return 0
340 }
341}
342
343fn test_is_bool() {
344 println('---- ${@FN} ----')
345 dog := Dog{}
346 assert is_dog(dog) == true
347 assert is_dog_or_cat(dog)
348 cat := Cat{}
349 assert is_dog(cat) == false
350 assert is_dog_or_cat(cat)
351 bird := Bird{}
352 assert is_dog(bird) == false
353 assert !is_dog_or_cat(bird)
354 assert is_dog_or_cat_or_bird(bird)
355}
356
357fn is_dog(a Animal) bool {
358 println("Got animal: '${a}'") // implicit call to 'str' function of implementations
359 println('with type: ${typeof(a).name}') // get implementation type (if possible)
360
361 // sample (additional checks) here
362 is_dog_or_cat := if a is Dog || a is Cat { true } else { false }
363 println('Animal is Dog or Cat: ${is_dog_or_cat}')
364
365 // use/test even another syntax
366 if a is Dog {
367 return true
368 } else {
369 return false
370 }
371}
372
373fn new_animal() Animal {
374 dog := Dog{}
375 return dog
376}
377
378fn new_animal2() Animal {
379 return new_animal()
380}
381
382/*
383// TODO
384fn animal_match(a Animal) {
385 match a {
386 Dog { println('(dog)') }
387 Cat { println('(cat)') }
388 else { println('(other)' }
389 }
390}
391*/
392
393interface II {
394mut:
395 my_field int
396}
397
398struct AA {
399 BB
400}
401
402struct BB {
403 pad [10]u8
404mut:
405 my_field int
406}
407
408// the opposite example of interface_mutability_receiver.vv
409// related to https://github.com/vlang/v/issues/1081 and https://github.com/vlang/v/issues/7338
410// test example code by https://github.com/nedpals copied and adapted from https://github.com/vlang/v/issues/7338
411// we accept immutable get_name even if the interface specified `mut` as it is consistent with function param behavior
412struct Dog2 {
413pub mut:
414 name string
415}
416
417fn (d Dog2) get_name() string {
418 return d.name
419}
420
421// mut get_name might be a bad example
422// we try to show that an interface can be more liberal in mut than the implementor,
423// while our error check is for the opposite case
424interface Animal2 {
425mut:
426 get_name() string
427}
428
429fn get_animal_name(mut a Animal2) string {
430 return a.get_name()
431}
432
433fn test_aa() {
434 println('---- ${@FN} ----')
435 mut aa := AA{}
436 mut ii := II(aa)
437 assert ii.my_field == 0
438 aa.my_field = 123
439 assert ii.my_field == 123
440 ii.my_field = 1234
441 assert aa.my_field == 1234
442 mut dog := Dog2{'Doggo'}
443 println(dog.name)
444 println(get_animal_name(mut dog))
445}
446
447type Text = string
448
449fn (t Text) display() string {
450 return t
451}
452
453interface Displayable {
454 display() string
455}
456
457fn print_displayable(ds ...Displayable) {
458 for d in ds {
459 println(d.display())
460 }
461}
462
463fn test_variadic_interface() {
464 print_displayable(Text('test'), Text('hehe'))
465}
466
467interface MyError {
468 print_error()
469}
470
471struct CustomError implements MyError {
472}
473
474fn (e CustomError) print_error() {
475}
476
477fn handle_implements(e MyError) {
478 e.print_error()
479}
480
481fn test_implements() {
482 e := CustomError{}
483 handle_implements(e)
484 println('implements ok')
485}
486
487interface VtableDrawable {
488 draw() int
489}
490
491interface VtableCalculator {
492 add(a int, b int) int
493 multiply(x int) int
494}
495
496struct VtablePoint {
497 x int
498 y int
499}
500
501fn (p VtablePoint) draw() int {
502 return p.x * 1000 + p.y
503}
504
505fn (p VtablePoint) add(a int, b int) int {
506 return p.x + p.y + a + b
507}
508
509fn (p VtablePoint) multiply(x int) int {
510 return (p.x + p.y) * x
511}
512
513fn test_interface_value_receiver_dispatch() {
514 p1 := VtablePoint{
515 x: 7
516 y: 3
517 }
518 d1 := VtableDrawable(p1)
519 assert d1.draw() == 7003
520
521 p2 := VtablePoint{
522 x: 15
523 y: 25
524 }
525 d2 := VtableDrawable(p2)
526 assert d2.draw() == 15025
527
528 p3 := VtablePoint{
529 x: 1
530 y: 1
531 }
532 d3 := VtableDrawable(p3)
533 assert d3.draw() + d3.draw() == 2002
534}
535
536fn test_interface_value_receiver_dispatch_with_params() {
537 calc := VtableCalculator(VtablePoint{
538 x: 10
539 y: 5
540 })
541 assert calc.add(3, 7) == 25
542 assert calc.multiply(4) == 60
543
544 calc2 := VtableCalculator(VtablePoint{
545 x: 2
546 y: 3
547 })
548 assert calc2.add(1, 1) == 7
549 assert calc2.multiply(10) == 50
550}
551