get rid of intellij.build.toolbox.litegen parameter and use BuildOptions.TOOLBOX_LITE...
[idea/community.git] / platform / configuration-store-impl / testSrc / xml / XmlSerializerTest.kt
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 @file:Suppress("PropertyName")
3
4 package com.intellij.configurationStore.xml
5
6 import com.intellij.configurationStore.StoredPropertyStateTest
7 import com.intellij.configurationStore.clearBindingCache
8 import com.intellij.configurationStore.deserialize
9 import com.intellij.configurationStore.serialize
10 import com.intellij.openapi.util.JDOMUtil
11 import com.intellij.openapi.util.text.StringUtil
12 import com.intellij.serialization.SerializationException
13 import com.intellij.testFramework.UsefulTestCase
14 import com.intellij.testFramework.assertConcurrent
15 import com.intellij.testFramework.assertions.Assertions.assertThat
16 import com.intellij.util.xmlb.Accessor
17 import com.intellij.util.xmlb.SerializationFilter
18 import com.intellij.util.xmlb.SkipDefaultsSerializationFilter
19 import com.intellij.util.xmlb.XmlSerializer
20 import com.intellij.util.xmlb.annotations.*
21 import junit.framework.TestCase
22 import org.intellij.lang.annotations.Language
23 import org.jdom.Element
24 import org.junit.Test
25 import org.junit.runner.RunWith
26 import org.junit.runners.Suite
27 import java.util.*
28
29 @RunWith(Suite::class)
30 @Suite.SuiteClasses(
31   XmlSerializerTest::class,
32   XmlSerializerMapTest::class,
33   XmlSerializerOldMapAnnotationTest::class,
34   XmlSerializerCollectionTest::class,
35   StoredPropertyStateTest::class,
36   KotlinXmlSerializerTest::class,
37   XmlSerializerConversionTest::class,
38   XmlSerializerListTest::class,
39   XmlSerializerSetTest::class,
40   ForbidSensitiveInformationTest::class
41 )
42 class XmlSerializerTestSuite
43
44 @Suppress("PropertyName")
45 internal class XmlSerializerTest {
46   @Test fun annotatedInternalVar() {
47     class Bean {
48       @MapAnnotation(surroundWithTag = false, surroundKeyWithTag = false, surroundValueWithTag = false)
49       var PLACES_MAP = TreeMap<String, String>()
50     }
51
52     val data = Bean()
53     data.PLACES_MAP.put("foo", "bar")
54     testSerializer("""
55     <Bean>
56       <option name="PLACES_MAP">
57         <entry key="foo" value="bar" />
58       </option>
59     </Bean>""", data)
60   }
61
62   @Test
63   fun testClearBindingCache() {
64     if (!UsefulTestCase.IS_UNDER_TEAMCITY) {
65       clearBindingCache()
66     }
67   }
68
69   @Test fun `no error if no accessors`() {
70     class EmptyBean
71
72     testSerializer("<EmptyBean />", EmptyBean())
73   }
74
75   @Test fun `suppress no accessors warn`() {
76     @Property(assertIfNoBindings = false)
77     class EmptyBean
78
79     testSerializer("<EmptyBean />", EmptyBean())
80   }
81
82   @Test fun publicFieldSerialization() {
83     val bean = BeanWithPublicFields()
84
85     testSerializer("<BeanWithPublicFields>\n  <option name=\"INT_V\" value=\"1\" />\n  <option name=\"STRING_V\" value=\"hello\" />\n</BeanWithPublicFields>", bean)
86
87     bean.INT_V = 2
88     bean.STRING_V = "bye"
89
90     testSerializer("<BeanWithPublicFields>\n  <option name=\"INT_V\" value=\"2\" />\n  <option name=\"STRING_V\" value=\"bye\" />\n</BeanWithPublicFields>", bean)
91   }
92
93   @Test fun publicFieldSerializationWithInheritance() {
94     val bean = BeanWithPublicFieldsDescendant()
95
96     testSerializer("""
97     <BeanWithPublicFieldsDescendant>
98       <option name="NEW_S" value="foo" />
99       <option name="INT_V" value="1" />
100       <option name="STRING_V" value="hello" />
101     </BeanWithPublicFieldsDescendant>""", bean)
102
103     bean.INT_V = 2
104     bean.STRING_V = "bye"
105     bean.NEW_S = "bar"
106
107     testSerializer("""<BeanWithPublicFieldsDescendant>
108   <option name="NEW_S" value="bar" />
109   <option name="INT_V" value="2" />
110   <option name="STRING_V" value="bye" />
111 </BeanWithPublicFieldsDescendant>""", bean)
112   }
113
114   private class BeanWithSubBean {
115     var bean1: BeanWithPublicFields? = BeanWithPublicFields()
116     var bean2: BeanWithPublicFields? = BeanWithPublicFields()
117   }
118
119   @Test fun subBeanSerialization() {
120     val bean = BeanWithSubBean()
121     testSerializer("""<BeanWithSubBean>
122   <option name="bean1">
123     <BeanWithPublicFields>
124       <option name="INT_V" value="1" />
125       <option name="STRING_V" value="hello" />
126     </BeanWithPublicFields>
127   </option>
128   <option name="bean2">
129     <BeanWithPublicFields>
130       <option name="INT_V" value="1" />
131       <option name="STRING_V" value="hello" />
132     </BeanWithPublicFields>
133   </option>
134 </BeanWithSubBean>""", bean)
135     bean.bean2!!.INT_V = 2
136     bean.bean2!!.STRING_V = "bye"
137
138     testSerializer("""<BeanWithSubBean>
139   <option name="bean1">
140     <BeanWithPublicFields>
141       <option name="INT_V" value="1" />
142       <option name="STRING_V" value="hello" />
143     </BeanWithPublicFields>
144   </option>
145   <option name="bean2">
146     <BeanWithPublicFields>
147       <option name="INT_V" value="2" />
148       <option name="STRING_V" value="bye" />
149     </BeanWithPublicFields>
150   </option>
151 </BeanWithSubBean>""", bean)
152   }
153
154   @Test fun subBeanSerializationAndSkipDefaults() {
155     val bean = BeanWithSubBean()
156     testSerializer("<BeanWithSubBean />", bean, SkipDefaultsSerializationFilter())
157   }
158
159   @Suppress("EqualsOrHashCode")
160   private class BeanWithEquals {
161     var STRING_V = "hello"
162
163     override fun equals(other: Any?): Boolean {
164       // any instance of this class is equal
165       @Suppress("SuspiciousEqualsCombination")
166       return this === other || (other != null && javaClass == other.javaClass)
167     }
168   }
169
170   @Test fun subBeanWithEqualsSerializationAndSkipDefaults() {
171     @Tag("bean")
172     class BeanWithSubBeanWithEquals {
173       @Suppress("unused")
174       var bean1: BeanWithPublicFields = BeanWithPublicFields()
175       var bean2: BeanWithEquals = BeanWithEquals()
176     }
177
178     val bean = BeanWithSubBeanWithEquals()
179     val filter = SkipDefaultsSerializationFilter()
180     testSerializer("<bean />", bean, filter)
181
182     bean.bean2.STRING_V = "new"
183     testSerializer("<bean />", bean, filter)
184   }
185
186   @Test fun nullFieldValue() {
187     val bean1 = BeanWithPublicFields()
188
189     testSerializer("""<BeanWithPublicFields>
190   <option name="INT_V" value="1" />
191   <option name="STRING_V" value="hello" />
192 </BeanWithPublicFields>""", bean1)
193
194     bean1.STRING_V = null
195
196     testSerializer("""<BeanWithPublicFields>
197   <option name="INT_V" value="1" />
198   <option name="STRING_V" />
199 </BeanWithPublicFields>""", bean1)
200
201     val bean2 = BeanWithSubBean()
202     bean2.bean1 = null
203     bean2.bean2 = null
204
205     testSerializer("""<BeanWithSubBean>
206   <option name="bean1" />
207   <option name="bean2" />
208 </BeanWithSubBean>""", bean2)
209   }
210
211   private data class BeanWithOption(@OptionTag("path") var PATH: String? = null)
212
213   @Test fun optionTag() {
214     val bean = BeanWithOption()
215     bean.PATH = "123"
216     testSerializer("<BeanWithOption>\n" + "  <option name=\"path\" value=\"123\" />\n" + "</BeanWithOption>", bean)
217   }
218
219   private data class BeanWithCustomizedOption(@OptionTag(tag = "setting", nameAttribute = "key", valueAttribute = "saved") var PATH: String? = null)
220
221   @Test fun customizedOptionTag() {
222     val bean = BeanWithCustomizedOption()
223     bean.PATH = "123"
224     testSerializer("<BeanWithCustomizedOption>\n" + "  <setting key=\"PATH\" saved=\"123\" />\n" + "</BeanWithCustomizedOption>", bean)
225   }
226
227   @Test fun propertySerialization() {
228     val bean = BeanWithProperty()
229     testSerializer("<BeanWithProperty>\n" + "  <option name=\"name\" value=\"James\" />\n" + "</BeanWithProperty>", bean)
230     bean.name = "Bond"
231     testSerializer("<BeanWithProperty>\n" + "  <option name=\"name\" value=\"Bond\" />\n" + "</BeanWithProperty>", bean)
232   }
233
234   private class BeanWithFieldWithTagAnnotation {
235     @Tag("name") var STRING_V = "hello"
236   }
237
238   @Test fun `parallel deserialization`() {
239     val e = Element("root").addContent(Element("name").setText("x"))
240     assertConcurrent(*Array(5) {
241       {
242         for (i in 0..9) {
243           val bean = deserialize<BeanWithFieldWithTagAnnotation>(e)
244           assertThat(bean).isNotNull()
245           assertThat(bean.STRING_V).isEqualTo("x")
246         }
247       }
248     })
249   }
250
251   class Complex {
252     var foo: Complex? = null
253   }
254
255   @Test fun `self class reference deserialization`() {
256     testSerializer("""
257     <Complex>
258       <option name="foo" />
259     </Complex>""", Complex())
260   }
261
262   @Test fun fieldWithTagAnnotation() {
263     val bean = BeanWithFieldWithTagAnnotation()
264     testSerializer("<BeanWithFieldWithTagAnnotation>\n" + "  <name>hello</name>\n" + "</BeanWithFieldWithTagAnnotation>", bean)
265     bean.STRING_V = "bye"
266     testSerializer("<BeanWithFieldWithTagAnnotation>\n" + "  <name>bye</name>\n" + "</BeanWithFieldWithTagAnnotation>", bean)
267   }
268
269   @Test fun escapeCharsInTagText() {
270     val bean = BeanWithFieldWithTagAnnotation()
271     bean.STRING_V = "a\nb\"<"
272
273     testSerializer("<BeanWithFieldWithTagAnnotation>\n" + "  <name>a\nb&quot;&lt;</name>\n" + "</BeanWithFieldWithTagAnnotation>", bean)
274   }
275
276   @Test fun escapeCharsInAttributeValue() {
277     val bean = BeanWithPropertiesBoundToAttribute()
278     bean.name = "a\nb\"<"
279     testSerializer("<BeanWithPropertiesBoundToAttribute count=\"3\" name=\"a&#10;b&quot;&lt;\" />", bean)
280   }
281
282   @Test fun shuffledDeserialize() {
283     var bean = BeanWithPublicFields()
284     bean.INT_V = 987
285     bean.STRING_V = "1234"
286
287     val element = serialize(bean)!!
288
289     val node = element.children.get(0)
290     element.removeContent(node)
291     element.addContent(node)
292
293     bean = deserialize(element)
294     assertThat(bean.INT_V).isEqualTo(987)
295     assertThat(bean.STRING_V).isEqualTo("1234")
296   }
297
298   @Test fun filterSerializer() {
299     val bean = BeanWithPublicFields()
300     assertSerializer(bean, "<BeanWithPublicFields>\n" + "  <option name=\"INT_V\" value=\"1\" />\n" + "</BeanWithPublicFields>", SerializationFilter { accessor, _ -> accessor.name.startsWith("I") })
301   }
302
303   @Test fun transient() {
304     class Bean {
305       @Suppress("unused")
306       var INT_V: Int = 1
307         @Transient
308         get
309
310       @Suppress("unused")
311       @Transient
312       fun getValue(): String = "foo"
313
314       var foo: String? = null
315     }
316
317     testSerializer("""<Bean>
318   <option name="foo" />
319 </Bean>""", Bean())
320   }
321
322   @Test fun propertyWithoutTagWithPrimitiveType() {
323     class BeanWithPropertyWithoutTagOnPrimitiveValue {
324       @Suppress("unused")
325       @Property(surroundWithTag = false)
326       var INT_V = 1
327     }
328
329     val bean = BeanWithPropertyWithoutTagOnPrimitiveValue()
330     try {
331       testSerializer("<BeanWithPropertyWithoutTagOnPrimitiveValue><name>hello</name></BeanWithPropertyWithoutTagOnPrimitiveValue>", bean)
332     }
333     catch (e: SerializationException) {
334       return
335     }
336
337     TestCase.fail("No Exception")
338   }
339
340   @Test fun propertyWithoutTag() {
341     @Tag("bean")
342     class BeanWithPropertyWithoutTag {
343       @Property(surroundWithTag = false)
344       var BEAN1 = BeanWithPublicFields()
345       var INT_V = 1
346     }
347
348     val bean = BeanWithPropertyWithoutTag()
349
350     testSerializer("""<bean>
351   <option name="INT_V" value="1" />
352   <BeanWithPublicFields>
353     <option name="INT_V" value="1" />
354     <option name="STRING_V" value="hello" />
355   </BeanWithPublicFields>
356 </bean>""", bean)
357
358     bean.INT_V = 2
359     bean.BEAN1.STRING_V = "junk"
360
361     testSerializer("""<bean>
362   <option name="INT_V" value="2" />
363   <BeanWithPublicFields>
364     <option name="INT_V" value="1" />
365     <option name="STRING_V" value="junk" />
366   </BeanWithPublicFields>
367 </bean>""", bean)
368   }
369
370   @Tag("bean")
371   private class BeanWithArrayWithoutAllTag {
372     @Property(surroundWithTag = false)
373     @XCollection(elementName = "vValue", valueAttributeName = "v")
374     var v = arrayOf("a", "b")
375
376     var intV = 1
377   }
378
379   @Test fun arrayWithoutAllTags() {
380     val bean = BeanWithArrayWithoutAllTag()
381
382     testSerializer("""<bean>
383   <option name="intV" value="1" />
384   <vValue v="a" />
385   <vValue v="b" />
386 </bean>""", bean)
387
388     bean.intV = 2
389     bean.v = arrayOf("1", "2", "3")
390
391     testSerializer("""<bean>
392   <option name="intV" value="2" />
393   <vValue v="1" />
394   <vValue v="2" />
395   <vValue v="3" />
396 </bean>""", bean)
397   }
398
399   @Test fun arrayWithoutAllTags2() {
400     @Tag("bean")
401     class BeanWithArrayWithoutAllTag2 {
402       @Property(surroundWithTag = false)
403       @XCollection(elementName = "vValue", valueAttributeName = "")
404       var v = arrayOf("a", "b")
405       var intV = 1
406     }
407
408     val bean = BeanWithArrayWithoutAllTag2()
409
410     testSerializer("""<bean>
411   <option name="intV" value="1" />
412   <vValue>a</vValue>
413   <vValue>b</vValue>
414 </bean>""", bean)
415
416     bean.intV = 2
417     bean.v = arrayOf("1", "2", "3")
418
419     testSerializer("""<bean>
420   <option name="intV" value="2" />
421   <vValue>1</vValue>
422   <vValue>2</vValue>
423   <vValue>3</vValue>
424 </bean>""", bean)
425   }
426
427   @Test fun deserializeFromFormattedXML() {
428     val bean = deserialize<BeanWithArrayWithoutAllTag>(JDOMUtil.load("""
429         <bean>
430         <option name="intV" value="2"/>
431         <vValue v="1"/>
432         <vValue v="2"/>
433         <vValue v="3"/>
434       </bean>"""))
435     assertThat(bean.intV).isEqualTo(2)
436     assertThat("[1, 2, 3]").isEqualTo(Arrays.asList(*bean.v).toString())
437   }
438
439   private class BeanWithPropertiesBoundToAttribute {
440     @Attribute("count")
441     var COUNT = 3
442     @Attribute("name")
443     var name = "James"
444     @Suppress("unused")
445     @Attribute("occupation")
446     var occupation: String? = null
447   }
448
449   @Test fun beanWithPrimitivePropertyBoundToAttribute() {
450     val bean = BeanWithPropertiesBoundToAttribute()
451
452     testSerializer("<BeanWithPropertiesBoundToAttribute count=\"3\" name=\"James\" />", bean)
453
454     bean.COUNT = 10
455     bean.name = "Bond"
456
457     testSerializer("<BeanWithPropertiesBoundToAttribute count=\"10\" name=\"Bond\" />", bean)
458   }
459
460
461   private class BeanWithPropertyFilter {
462     @Property(filter = PropertyFilterTest::class) var STRING_V: String = "hello"
463   }
464
465   private class PropertyFilterTest : SerializationFilter {
466     override fun accepts(accessor: Accessor, bean: Any): Boolean {
467       return accessor.readUnsafe(bean) != "skip"
468     }
469   }
470
471   @Test fun propertyFilter() {
472     val bean = BeanWithPropertyFilter()
473
474     testSerializer("<BeanWithPropertyFilter>\n" + "  <option name=\"STRING_V\" value=\"hello\" />\n" + "</BeanWithPropertyFilter>", bean)
475
476     bean.STRING_V = "bye"
477
478     testSerializer("<BeanWithPropertyFilter>\n" + "  <option name=\"STRING_V\" value=\"bye\" />\n" + "</BeanWithPropertyFilter>", bean)
479
480     bean.STRING_V = "skip"
481
482     assertSerializer(bean, "<BeanWithPropertyFilter />", null)
483   }
484
485   private class BeanWithJDOMElement {
486     var STRING_V: String = "hello"
487     @Tag("actions") var actions: Element? = null
488   }
489
490   @Test fun serializeJDOMElementField() {
491     val element = BeanWithJDOMElement()
492     element.STRING_V = "a"
493     element.actions = Element("x").addContent(Element("a")).addContent(Element("b"))
494     assertSerializer(element, """<BeanWithJDOMElement>
495   <option name="STRING_V" value="a" />
496   <actions>
497     <a />
498     <b />
499   </actions>
500 </BeanWithJDOMElement>""", null)
501
502     element.actions = null
503     assertSerializer(element, """<BeanWithJDOMElement>
504   <option name="STRING_V" value="a" />
505 </BeanWithJDOMElement>""", null)
506   }
507
508   @Test fun deserializeJDOMElementField() {
509     val bean = deserialize<BeanWithJDOMElement>(JDOMUtil.load(
510       "<BeanWithJDOMElement><option name=\"STRING_V\" value=\"bye\"/><actions><action/><action/></actions></BeanWithJDOMElement>"))
511
512     assertThat(bean.STRING_V).isEqualTo("bye")
513     assertThat(bean.actions).isNotNull
514     assertThat(bean.actions!!.getChildren("action")).hasSize(2)
515   }
516
517   class BeanWithJDOMElementArray {
518     var STRING_V: String = "hello"
519     @Tag("actions") var actions: Array<Element>? = null
520   }
521
522   @Test fun jdomElementArrayField() {
523     val text = "<BeanWithJDOMElementArray>\n" + "  <option name=\"STRING_V\" value=\"bye\" />\n" + "  <actions>\n" + "    <action />\n" + "    <action />\n" + "  </actions>\n" + "  <actions>\n" + "    <action />\n" + "  </actions>\n" + "</BeanWithJDOMElementArray>"
524     val bean = deserialize<BeanWithJDOMElementArray>(JDOMUtil.load(text))
525
526     TestCase.assertEquals("bye", bean.STRING_V)
527     TestCase.assertNotNull(bean.actions)
528     TestCase.assertEquals(2, bean.actions!!.size)
529     TestCase.assertEquals(2, bean.actions!![0].children.size)
530     TestCase.assertEquals(1, bean.actions!![1].children.size)
531
532     assertSerializer(bean, text, null)
533
534     bean.actions = null
535     val newText = "<BeanWithJDOMElementArray>\n" + "  <option name=\"STRING_V\" value=\"bye\" />\n" + "</BeanWithJDOMElementArray>"
536     testSerializer(newText, bean)
537
538     bean.actions = emptyArray()
539     testSerializer(newText, bean)
540   }
541
542   @Test fun textAnnotation() {
543     val bean = BeanWithTextAnnotation()
544
545     testSerializer("<BeanWithTextAnnotation>\n" + "  <option name=\"INT_V\" value=\"1\" />\n" + "  hello\n" + "</BeanWithTextAnnotation>", bean)
546
547     bean.INT_V = 2
548     bean.STRING_V = "bye"
549
550     testSerializer("<BeanWithTextAnnotation>\n" + "  <option name=\"INT_V\" value=\"2\" />\n" + "  bye\n" + "</BeanWithTextAnnotation>", bean)
551   }
552
553   private class BeanWithEnum {
554     enum class TestEnum {
555       VALUE_1,
556       VALUE_2,
557       VALUE_3
558     }
559
560     var FLD = TestEnum.VALUE_1
561   }
562
563   @Test fun enums() {
564     val bean = BeanWithEnum()
565
566     testSerializer("<BeanWithEnum>\n" + "  <option name=\"FLD\" value=\"VALUE_1\" />\n" + "</BeanWithEnum>", bean)
567
568     bean.FLD = BeanWithEnum.TestEnum.VALUE_3
569
570     testSerializer("<BeanWithEnum>\n" + "  <option name=\"FLD\" value=\"VALUE_3\" />\n" + "</BeanWithEnum>", bean)
571   }
572
573   @Tag("condition")
574   private class ConditionBean {
575     @Attribute("expression")
576     var newCondition: String? = null
577     @Text
578     var oldCondition: String? = null
579   }
580
581   @Test fun conversionFromTextToAttribute() {
582     @Tag("bean")
583     class Bean {
584       @Property(surroundWithTag = false)
585       var conditionBean = ConditionBean()
586     }
587
588     var bean = Bean()
589     bean.conditionBean.oldCondition = "2+2"
590     testSerializer("<bean>\n  <condition>2+2</condition>\n</bean>", bean)
591
592     bean = Bean()
593     bean.conditionBean.newCondition = "2+2"
594     testSerializer("<bean>\n  <condition expression=\"2+2\" />\n" + "</bean>", bean)
595   }
596
597   @Test fun `no wrap`() {
598     @Tag("bean")
599     class Bean {
600       @Property(flat = true)
601       var conditionBean = ConditionBean()
602     }
603
604     var bean = Bean()
605     bean.conditionBean.oldCondition = "2+2"
606     testSerializer("<bean>2+2</bean>", bean)
607
608     bean = Bean()
609     bean.conditionBean.newCondition = "2+2"
610     testSerializer("<bean expression=\"2+2\" />", bean)
611   }
612
613   @Test fun deserializeInto() {
614     val bean = BeanWithPublicFields()
615     bean.STRING_V = "zzz"
616
617     XmlSerializer.deserializeInto(bean,
618                                   JDOMUtil.load("<BeanWithPublicFields><option name=\"INT_V\" value=\"999\"/></BeanWithPublicFields>"))
619
620     assertThat(bean.INT_V).isEqualTo(999)
621     assertThat(bean.STRING_V).isEqualTo("zzz")
622   }
623
624   @Test fun defaultAttributeName() {
625     class BeanWithDefaultAttributeName {
626       @Suppress("unused")
627       @Attribute fun getFoo() = "foo"
628
629       @Suppress("unused")
630       fun setFoo(@Suppress("UNUSED_PARAMETER") value: String) {
631       }
632     }
633
634     testSerializer("<BeanWithDefaultAttributeName foo=\"foo\" />", BeanWithDefaultAttributeName())
635   }
636
637   @Test
638   fun ordered() {
639     @Tag("bean")
640     class Bean {
641       @Attribute
642       var ab: String? = null
643
644       @Attribute
645       var module: String? = null
646
647       @Suppress("unused")
648       @Attribute
649       var ac: String? = null
650     }
651
652     val bean = Bean()
653     bean.module = "module"
654     bean.ab = "ab"
655     testSerializer("<bean ab=\"ab\" module=\"module\" />", bean, SkipDefaultsSerializationFilter())
656   }
657
658   @Test
659   fun cdataAfterNewLine() {
660     @Tag("bean")
661     data class Bean(@Tag val description: String? = null)
662
663     var bean = deserialize<Bean>(JDOMUtil.load("""<bean>
664       <description>
665         <![CDATA[
666         <h4>Node.js integration</h4>
667         ]]>
668       </description>
669     </bean>"""))
670     assertThat(bean.description).isEqualToIgnoringWhitespace("<h4>Node.js integration</h4>")
671
672     bean = deserialize(JDOMUtil.load("""<bean><description><![CDATA[<h4>Node.js integration</h4>]]></description></bean>"""))
673     assertThat(bean.description).isEqualTo("<h4>Node.js integration</h4>")
674   }
675
676 //  @Test
677 //  fun dataClass() {
678 //    data class ConnectionKey(val server: String, val client: String, val user: String) {
679 //      override fun toString() = "$server, $user@$client"
680 //    }
681 //
682 //    @Tag("bean")
683 //    class ConfigBean {
684 //      @JvmField
685 //      var listMappings: MutableMap<ConnectionKey, String> = THashMap()
686 //    }
687 //
688 //    val bean = ConfigBean()
689 //    bean.listMappings.put(ConnectionKey("localhost", "bad", "ivan"), "bar")
690 //    testSerializer("""
691 //    <bean>
692 //      <option name="listMappings">
693 //        <map />
694 //      </option>
695 //    </bean>
696 //      """, bean)
697 //  }
698 }
699
700 internal fun assertSerializer(bean: Any, expected: String, filter: SerializationFilter? = null, description: String = "Serialization failure"): Element {
701   val element = serialize(bean, filter, createElementIfEmpty = true)!!
702   assertThat(element).`as`(description).isEqualTo(expected)
703   return element
704 }
705
706 fun <T: Any> testSerializer(@Language("XML") expectedText: String, bean: T, filter: SerializationFilter? = null): T {
707   val expectedTrimmed = expectedText.trimIndent()
708   val element = assertSerializer(bean, expectedTrimmed, filter)
709
710   // test deserializer
711   val o = element.deserialize(bean.javaClass)
712   assertSerializer(o, expectedTrimmed, filter, "Deserialization failure")
713   return o
714 }
715
716 internal open class BeanWithPublicFields(@JvmField var INT_V: Int = 1, @JvmField var STRING_V: String? = "hello") : Comparable<BeanWithPublicFields> {
717   override fun compareTo(other: BeanWithPublicFields) = StringUtil.compare(STRING_V, other.STRING_V, false)
718 }
719
720 internal class BeanWithTextAnnotation {
721   var INT_V: Int = 1
722   @Text var STRING_V: String = "hello"
723
724   constructor(INT_V: Int, STRING_V: String) {
725     this.INT_V = INT_V
726     this.STRING_V = STRING_V
727   }
728
729   constructor()
730 }
731
732 internal class BeanWithProperty {
733   var name: String = "James"
734
735   constructor()
736
737   constructor(name: String) {
738     this.name = name
739   }
740 }
741
742 internal class BeanWithPublicFieldsDescendant(@JvmField var NEW_S: String? = "foo") : BeanWithPublicFields()