• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pureconfig / pureconfig / 17584197229

09 Sep 2025 01:27PM UTC coverage: 94.027% (-0.7%) from 94.686%
17584197229

Pull #1839

web-flow
Merge f6adfff74 into ae9e60568
Pull Request #1839: Additional mode for semiauto derivation

6 of 6 new or added lines in 2 files covered. (100.0%)

4 existing lines in 3 files now uncovered.

2377 of 2528 relevant lines covered (94.03%)

2.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/testkit/src/main/scala/pureconfig/ConfigConvertChecks.scala
1
package pureconfig
2

3
import scala.reflect.ClassTag
4

5
import com.typesafe.config.{ConfigRenderOptions, ConfigValue, ConfigValueFactory}
6
import org.scalacheck.Arbitrary
7
import org.scalactic.Equality
8
import org.scalatest.EitherValues
9
import org.scalatest.flatspec.AnyFlatSpec
10
import org.scalatest.matchers.should.Matchers
11
import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks
12

13
import pureconfig.error.{ConfigReaderFailures, ConvertFailure, FailureReason}
14

15
/** Add utilities to a scalatest `FlatSpec` to test `ConfigConvert` instances
16
  */
17
trait ConfigConvertChecks { this: AnyFlatSpec with Matchers with ScalaCheckDrivenPropertyChecks with EitherValues =>
18

19
  /** For each value of type `A`, check that the value produced by converting to and then from `ConfigValue` is the same
20
    * of the original value
21
    *
22
    * Note that this method doesn't check all the values but only the values that can be created by `Arbitrary[A]` and
23
    * only the `ConfigValue` created by `ConfigConvert[A].to`. While `Arbitrary[A]` is usually comprehensive,
24
    * `ConfigConvert[A].from` could support different kind of values that `ConfigConvert[A].to` doesn't produce because,
25
    * for instance, multiple representation of `a: A` are possible. Use [[checkRead]] for those representations.
26
    */
27
  def checkArbitrary[A](implicit
1✔
28
      cc: ConfigConvert[A],
29
      arb: Arbitrary[A],
30
      tpe: TypeStringCompat[A],
31
      equality: Equality[A]
32
  ): Unit =
33
    it should s"read an arbitrary ${tpe.typeName}" in forAll { (a: A) =>
2✔
34
      cc.from(cc.to(a)).value shouldEqual a
2✔
35
    }
36

37
  /** A more generic version of [[checkArbitrary]] where the type which will be written as `ConfigValue` is different
38
    * from the type which will be read from that `ConfigValue`. The idea being is to test the reading part of a
39
    * `ConfigConvert` by providing another type for which it's easy to create `Arbitrary` instances and write the values
40
    * to a configuration.
41
    *
42
    * For instance, to test that `Double` can be read from percentages, like `"42 %"`, we can create a dummy
43
    * [[pureconfig.data.Percentage]] class which contains an integer from `0` to `100`, write that percentage to a
44
    * `ConfigValue` representing a `String` and then try to read the percentage from the `ConfigValue` via
45
    * `ConfigConvert[Double].from`. Creating an instance of `Arbitrary[Percentage]` is simple, same for
46
    * `ConfigConvert[Percentage]`.
47
    *
48
    * @param f
49
    *   a function used to convert a value of type `T2` to a value of type `T1`. The result of the conversion to and
50
    *   from a `ConfigValue` will be tested against the output of this function.
51
    * @param cr
52
    *   the `ConfigConvert` used to read a value from a `ConfigValue`. This is the instance that we want to test
53
    * @param cw
54
    *   the `ConfigConvert` used to write a value to a `ConfigValue`. This is the dummy instance used to test `cr`
55
    * @param arb
56
    *   the `Arbitrary` used to generate values to write a `ConfigValue` via `cw`
57
    */
58
  def checkArbitrary2[A, B](f: B => A)(implicit
1✔
59
      cr: ConfigConvert[A],
60
      cw: ConfigConvert[B],
61
      arb: Arbitrary[B],
62
      tpe1: TypeStringCompat[A],
63
      tpe2: TypeStringCompat[B],
64
      equality: Equality[A]
65
  ): Unit =
66
    it should s"read a ${tpe1.typeName} from an arbitrary ${tpe2.typeName}" in forAll { (b: B) =>
2✔
67
      cr.from(cw.to(b)).value shouldEqual f(b)
2✔
68
    }
69

70
  /** For each pair of value of type `A` and `ConfigValue`, check that `ConfigReader[A].from` successfully converts the
71
    * latter into to former. Useful to test specific values
72
    */
73
  def checkRead[A: Equality](
1✔
74
      reprsToValues: (ConfigValue, A)*
75
  )(implicit cr: ConfigReader[A], tpe: TypeStringCompat[A]): Unit =
76
    for ((repr, value) <- reprsToValues) {
2✔
77
      it should s"read the value $value of type ${tpe.typeName} from ${repr.render(ConfigRenderOptions.concise())}" in {
2✔
78
        cr.from(repr).value shouldEqual value
2✔
79
      }
80
    }
81

82
  /** Similar to [[checkRead]] but work on ConfigValues of type String */
83
  def checkReadString[A: ConfigReader: TypeStringCompat: Equality](strsToValues: (String, A)*): Unit =
1✔
84
    checkRead[A](strsToValues.map { case (s, a) => ConfigValueFactory.fromAnyRef(s) -> a }: _*)
2✔
85

86
  /** For each pair of value of type `A` and `ConfigValue`, check that `ConfigWriter[A].to` successfully converts the
87
    * former into the latter. Useful to test specific values
88
    */
89
  def checkWrite[A](
1✔
90
      valuesToReprs: (A, ConfigValue)*
91
  )(implicit cw: ConfigWriter[A], tpe: TypeStringCompat[A]): Unit =
92
    for ((value, repr) <- valuesToReprs) {
2✔
93
      it should s"write the value $value of type ${tpe.typeName} to ${repr.render(ConfigRenderOptions.concise())}" in {
2✔
94
        cw.to(value) shouldEqual repr
2✔
95
      }
96
    }
97

98
  /** Similar to [[checkWrite]] but work on ConfigValues of type String */
99
  def checkWriteString[A: ConfigWriter: TypeStringCompat](valuesToStrs: (A, String)*): Unit =
1✔
100
    checkWrite[A](valuesToStrs.map { case (a, s) => a -> ConfigValueFactory.fromAnyRef(s) }: _*)
2✔
101

102
  /** For each pair of value of type `A` and `ConfigValue`, check that `ConfigReader[A].from` successfully converts the
103
    * latter into to former and `ConfigWriter[A].to` successfully converts the former into the latter.
104
    */
105
  def checkReadWrite[A: ConfigReader: ConfigWriter: TypeStringCompat: Equality](
1✔
106
      reprsValues: (ConfigValue, A)*
107
  ): Unit = {
108
    checkRead[A](reprsValues: _*)
2✔
109
    checkWrite[A](reprsValues.map(_.swap): _*)
2✔
110
  }
111

112
  /** Similar to [[checkReadWrite]] but work on ConfigValues of type String */
113
  def checkReadWriteString[A: ConfigReader: ConfigWriter: TypeStringCompat: Equality](
1✔
114
      strsValues: (String, A)*
115
  ): Unit = {
116
    checkReadString[A](strsValues: _*)
2✔
117
    checkWriteString[A](strsValues.map(_.swap): _*)
2✔
118
  }
119

120
  /** Check that `cc` returns error of type `E` when trying to read each value passed with `values`
121
    *
122
    * @param values
123
    *   the values that should not be conver
124
    * @param cr
125
    *   the `ConfigConvert` to test
126
    */
127
  def checkFailure[A, E <: FailureReason](
1✔
128
      values: ConfigValue*
129
  )(implicit cr: ConfigReader[A], tpe: TypeStringCompat[A], eTag: ClassTag[E]): Unit =
130
    for (value <- values) {
2✔
131
      it should s"fail when it tries to read a value of type ${tpe.typeName} " +
2✔
132
        s"from ${value.render(ConfigRenderOptions.concise())}" in {
2✔
133
          val result = cr.from(value)
2✔
134
          result.left.value.toList should have size 1
2✔
UNCOV
135
          result.left.value.head should matchPattern { case ConvertFailure(_: E, _, _) => }
1✔
136
        }
137
    }
138

139
  /** For each pair of `ConfigValue` and `ConfigReaderFailures`, check that `cr` fails with the provided errors when
140
    * trying to read the provided `ConfigValue`.
141
    */
142
  def checkFailures[A](
1✔
143
      valuesToErrors: (ConfigValue, ConfigReaderFailures)*
144
  )(implicit cr: ConfigReader[A], tpe: TypeStringCompat[A]): Unit =
145
    for ((value, errors) <- valuesToErrors) {
2✔
146
      it should s"fail when it tries to read a value of type ${tpe.typeName} " +
2✔
147
        s"from ${value.render(ConfigRenderOptions.concise())}" in {
2✔
148
          cr.from(value).left.value.toList should contain theSameElementsAs errors.toList
2✔
149
        }
150
    }
151
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc