1 /*
2  * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.serialization
6 
7 import kotlinx.serialization.descriptors.SerialDescriptor
8 import kotlinx.serialization.encoding.AbstractDecoder
9 import kotlinx.serialization.encoding.CompositeDecoder
10 import kotlinx.serialization.modules.EmptySerializersModule
11 import kotlinx.serialization.modules.SerializersModule
12 import kotlin.test.Test
13 import kotlin.test.assertEquals
14 import kotlin.test.assertFails
15 
16 @Suppress("RedundantSetter", "RedundantGetter", "JoinDeclarationAndAssignment")
17 class CustomPropertyAccessorsTest {
18     @Serializable
19     class VarPropertiesClass {
20         var simple: String = "initial1"
21         var simpleInferred = "initial2"
22 
23         var getterSetter: String = "initial3"
24             set(value) {
25                 field = value
26             }
27             get() = field
28 
29         var getterSetterInferred = "initial4"
30             set(value) {
31                 field = value
32             }
33             get() {
34                 return field
35             }
36 
37         var setter: String = "initial5"
38             set(value) {
39                 field = value
40             }
41 
42         var deferredInit: String
43 
44         var getterDeferredInit: String
45             get() {
46                 return field
47             }
48 
49 
50         var noBackingField: String
51             set(value) {
52                 println(value)
53             }
54             get() {
55                 return "initial8"
56             }
57 
58         init {
59             deferredInit = "initial6"
60             getterDeferredInit = "initial7"
61         }
62 
63     }
64 
65     @Serializable
66     class ValPropertiesClass {
67         val simple: String = "initial1"
68         val simpleInferred = "initial2"
69 
70         val getter: String = "initial3"
71             get() {
72                 return field
73             }
74 
75         val getterInferred = "initial4"
76             get() {
77                 return field
78             }
79 
80         val deferredInit: String
81 
82         val noBackingField: String
83             get() {
84                 return "initial6"
85             }
86 
87         init {
88             deferredInit = "initial5"
89         }
90     }
91 
92     /*
93 
94          This class can't be instantiated because it's hard to check val property with deferred init having backing
95          field on constructor resolve stage. So synthetic constructor's signature differs from it's body and it always
96          throw exception during call.
97          In IR back-end this class would not be compiled.
98          @Serializable
99          class BrokenValPropertiesClass {
100              private val getterDeferredInit: String
101                  get() {
102                      return field
103                  }
104              init {
105                  getterDeferredInit = "initial6"
106              }
107          }
108      */
109 
110 
111     private class CommonStringDecoder(private val elementCount: Int) : AbstractDecoder() {
112         override val serializersModule: SerializersModule = EmptySerializersModule()
113         private var elementIndex = 0
114 
decodeStringnull115         override fun decodeString(): String {
116             return "decoded$elementIndex"
117         }
118 
decodeElementIndexnull119         override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
120             if (elementIndex == elementCount) return CompositeDecoder.DECODE_DONE
121             return elementIndex++
122         }
123 
decodeSequentiallynull124         override fun decodeSequentially(): Boolean = false
125     }
126 
127 
128     @Test
129     fun testVarProperties() {
130         val varPropertiesClass = VarPropertiesClass.serializer().deserialize(CommonStringDecoder(7))
131 
132         assertEquals("decoded1", varPropertiesClass.simple)
133         assertEquals("decoded2", varPropertiesClass.simpleInferred)
134         assertEquals("decoded3", varPropertiesClass.getterSetter)
135         assertEquals("decoded4", varPropertiesClass.getterSetterInferred)
136         assertEquals("decoded5", varPropertiesClass.setter)
137 
138         // properties with deferred init always has value from init block - deserialize value ignored
139         assertEquals("initial6", varPropertiesClass.deferredInit)
140         assertEquals("initial7", varPropertiesClass.getterDeferredInit)
141         assertEquals("initial8", varPropertiesClass.noBackingField)
142     }
143 
144     @Test
testValPropertiesnull145     fun testValProperties() {
146         val valPropertiesClass = ValPropertiesClass.serializer().deserialize(CommonStringDecoder(5))
147 
148         assertEquals("decoded1", valPropertiesClass.simple)
149         assertEquals("decoded2", valPropertiesClass.simpleInferred)
150         assertEquals("decoded3", valPropertiesClass.getter)
151         assertEquals("decoded4", valPropertiesClass.getterInferred)
152 
153         // properties with deferred init always has value from init block - deserialize value ignored
154         assertEquals("initial5", valPropertiesClass.deferredInit)
155         assertEquals("initial6", valPropertiesClass.noBackingField)
156     }
157 }
158