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

ahamez / protox / bdada51666f6bc061dc17a60c7545b810bd6a517

20 Aug 2025 07:00PM UTC coverage: 95.521% (-0.1%) from 95.652%
bdada51666f6bc061dc17a60c7545b810bd6a517

push

github

ahamez
chore: ignore current SAFE warnings

Protox needs to generate atoms from fields name

Protox relies on macros

853 of 893 relevant lines covered (95.52%)

21180.58 hits per line

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

96.67
/test/support/random_init.ex
1
# credo:disable-for-this-file Credo.Check.Refactor.CyclomaticComplexity
2
# credo:disable-for-this-file Credo.Check.Refactor.Nesting
3
defmodule Protox.RandomInit do
4
  @moduledoc false
5

6
  import Bitwise
7

8
  alias Protox.{Field, OneOf, Scalar}
9
  alias StreamData, as: SD
10

11
  def generate_msg(mod) do
12
    gen =
25✔
13
      SD.bind(generate_fields_values(mod), fn fields ->
14
        SD.constant(generate_struct(mod, fields))
25✔
15
      end)
16

17
    gen
18
    |> SD.resize(5)
19
    |> Enum.at(0)
25✔
20
  end
21

22
  # ------------------------------------------------------------------- #
23

24
  # Recursively generate the sub messages of mod
25
  def generate_struct(mod, nil), do: struct!(mod)
36✔
26

27
  def generate_struct(mod, fields) when is_list(fields) do
28
    sub_msgs =
82,308✔
29
      mod.schema().fields
82,308✔
30
      |> Map.values()
31
      # Get all sub messages
32
      |> Enum.filter(fn %Field{} = field ->
33
        case {field.kind, field.type} do
1,557,499✔
34
          {:map, {_, {:message, _}}} -> true
19,054✔
35
          {_, {:message, _}} -> true
378,131✔
36
          _ -> false
1,160,314✔
37
        end
38
      end)
39
      # Transform into a map for lookup
40
      |> Enum.reduce(%{}, fn %Field{} = field, acc ->
41
        case field.kind do
397,185✔
42
          %Scalar{} ->
43
            {:message, sub_msg} = field.type
197,422✔
44
            Map.put(acc, field.name, %Scalar{default_value: sub_msg})
197,422✔
45

46
          :unpacked ->
47
            {:message, sub_msg} = field.type
171,182✔
48
            Map.put(acc, field.name, {:repeated, sub_msg})
171,182✔
49

50
          :map ->
51
            {_, {:message, sub_msg}} = field.type
19,054✔
52
            Map.put(acc, field.name, {:map, sub_msg})
19,054✔
53

54
          %OneOf{parent: oneof_name} ->
55
            {:message, sub_msg} = field.type
9,527✔
56

57
            Map.update(
9,527✔
58
              acc,
59
              oneof_name,
60
              # initial insertion
61
              %OneOf{parent: %{field.name => sub_msg}},
9,527✔
62
              fn %OneOf{parent: sub_map} ->
63
                %OneOf{parent: Map.put(sub_map, field.name, sub_msg)}
×
64
              end
65
            )
66
        end
67
      end)
68

69
    new_fields =
82,308✔
70
      Enum.reduce(fields, [], fn {field_name, val}, acc ->
71
        case sub_msgs[field_name] do
188,982✔
72
          # Not a sub message, no transformation and recursion needed
73
          nil ->
141,569✔
74
            [{field_name, val} | acc]
75

76
          %OneOf{parent: sub_map} ->
77
            if val == nil do
777✔
78
              [{field_name, nil} | acc]
79
            else
80
              {sub_field_name, val} = val
683✔
81

82
              case sub_map[sub_field_name] do
683✔
83
                # the enclosing oneof contains one sub message, but sub_map does not
84
                # know about sub non-messages entries, thus we need to add them manually
85
                nil ->
619✔
86
                  [{field_name, {sub_field_name, val}} | acc]
87

88
                sub_msg ->
64✔
89
                  [{field_name, {sub_field_name, generate_struct(sub_msg, val)}} | acc]
90
              end
91
            end
92

93
          %Scalar{default_value: sub_msg} ->
94
            if val == nil do
31,208✔
95
              [{field_name, nil} | acc]
96
            else
97
              [{field_name, generate_struct(sub_msg, val)} | acc]
98
            end
99

100
          {:map, sub_msg} ->
101
            val =
1,554✔
102
              val
103
              |> Map.new(fn {k, msg_val} -> {k, generate_struct(sub_msg, msg_val)} end)
13,529✔
104

105
            [{field_name, val} | acc]
106

107
          {:repeated, sub_msg} ->
108
            val = Enum.map(val, fn msg_val -> generate_struct(sub_msg, msg_val) end)
13,874✔
109
            [{field_name, val} | acc]
110
        end
111
      end)
112

113
    struct!(mod, new_fields)
82,308✔
114
  end
115

116
  # ------------------------------------------------------------------- #
117

118
  @well_known_types [
119
    Google.Protobuf.BoolValue,
120
    Google.Protobuf.BytesValue,
121
    Google.Protobuf.DoubleValue,
122
    Google.Protobuf.Duration,
123
    Google.Protobuf.FieldMask,
124
    Google.Protobuf.FloatValue,
125
    Google.Protobuf.Int32Value,
126
    Google.Protobuf.Int64Value,
127
    Google.Protobuf.ListValue,
128
    Google.Protobuf.NullValue,
129
    Google.Protobuf.StringValue,
130
    Google.Protobuf.Struct,
131
    Google.Protobuf.Timestamp,
132
    Google.Protobuf.UInt32Value,
133
    Google.Protobuf.UInt64Value,
134
    Google.Protobuf.Value
135
  ]
136

137
  def generate_fields(mod, depth \\ 2) do
138
    do_generate([], Map.values(mod.schema().fields), depth)
525✔
139
  end
140

141
  def generate_fields_values(mod, depth \\ 2) do
142
    generate_fields(mod, depth) |> resolve_generators()
525✔
143
  end
144

145
  defp resolve_generators(%StreamData{} = gen), do: gen
4,995✔
146

147
  defp resolve_generators(term) when is_list(term) do
148
    Enum.reduce(Enum.reverse(term), SD.constant([]), fn elem, acc_gen ->
6,840✔
149
      SD.bind(resolve_generators(elem), fn v ->
17,595✔
150
        SD.map(acc_gen, fn acc -> [v | acc] end)
567,099✔
151
      end)
152
    end)
153
  end
154

155
  defp resolve_generators(term) when is_map(term) do
156
    term
157
    |> Map.to_list()
158
    |> resolve_generators()
159
    |> SD.map(&Map.new/1)
×
160
  end
161

162
  defp resolve_generators(term) when is_tuple(term) do
163
    term
164
    |> Tuple.to_list()
165
    |> resolve_generators()
166
    |> SD.map(&List.to_tuple/1)
5,865✔
167
  end
168

169
  defp resolve_generators(term), do: SD.constant(term)
6,285✔
170

171
  defp do_generate(acc, _fields, 0), do: acc
285✔
172
  defp do_generate(acc, [], _depth), do: acc
240✔
173

174
  defp do_generate(acc, [%Field{kind: %OneOf{parent: oneof_name}} | _] = fields, depth) do
175
    {oneof_list, fields} =
40✔
176
      Enum.split_with(fields, fn %Field{} = field ->
177
        case field.kind do
5,330✔
178
          %OneOf{parent: ^oneof_name} -> true
390✔
179
          _ -> false
4,940✔
180
        end
181
      end)
182

183
    acc
184
    |> do_generate_oneof(oneof_name, oneof_list, depth)
185
    |> do_generate(fields, depth)
40✔
186
  end
187

188
  defp do_generate(acc, [field | fields], depth) do
189
    do_generate([{field.name, get_gen(depth, field.kind, field.type)} | acc], fields, depth)
5,825✔
190
  end
191

192
  defp do_generate_oneof(acc, oneof_name, oneof_list, depth) do
193
    generators =
40✔
194
      Enum.map(oneof_list, fn %Field{kind: %OneOf{parent: _}} = field ->
195
        gen = get_gen(depth, %Scalar{default_value: :dummy}, field.type)
390✔
196
        SD.map(gen, fn v -> {field.name, v} end)
390✔
197
      end)
198

199
    [{oneof_name, SD.one_of([SD.constant(nil) | generators])} | acc]
200
  end
201

202
  defp get_gen(_depth, %Scalar{}, {:enum, e}) do
203
    e.constants() |> Map.new() |> Map.values() |> SD.member_of()
295✔
204
  end
205

206
  defp get_gen(_depth, %Scalar{}, :bool), do: SD.boolean()
170✔
207

208
  defp get_gen(_depth, %Scalar{}, :int32), do: SD.integer()
1,100✔
209
  defp get_gen(_depth, %Scalar{}, :int64), do: SD.integer()
130✔
210
  defp get_gen(_depth, %Scalar{}, :sint32), do: SD.integer()
130✔
211
  defp get_gen(_depth, %Scalar{}, :sint64), do: SD.integer()
130✔
212
  defp get_gen(_depth, %Scalar{}, :sfixed32), do: SD.integer()
130✔
213
  defp get_gen(_depth, %Scalar{}, :sfixed64), do: SD.integer()
130✔
214
  defp get_gen(_depth, %Scalar{}, :fixed32), do: SD.integer(0..((1 <<< 32) - 1))
130✔
215
  defp get_gen(_depth, %Scalar{}, :fixed64), do: SD.integer(0..((1 <<< 64) - 1))
130✔
216

217
  defp get_gen(_depth, %Scalar{}, :uint32), do: SD.integer(0..((1 <<< 32) - 1))
185✔
218
  defp get_gen(_depth, %Scalar{}, :uint64), do: SD.integer(0..((1 <<< 64) - 1))
170✔
219

220
  defp get_gen(_depth, %Scalar{}, :float), do: gen_float()
130✔
221
  defp get_gen(_depth, %Scalar{}, :double), do: gen_double()
130✔
222

223
  defp get_gen(_depth, %Scalar{}, :bytes), do: SD.binary()
160✔
224
  defp get_gen(_depth, %Scalar{}, :string), do: SD.string(:printable)
480✔
225

226
  defp get_gen(_depth, %Scalar{}, {:message, sub_msg}) when sub_msg in @well_known_types do
420✔
227
    nil
228
  end
229

230
  defp get_gen(depth, %Scalar{}, {:message, sub_msg}) do
231
    SD.one_of([SD.constant(nil), generate_fields_values(sub_msg, depth - 1)])
305✔
232
  end
233

234
  defp get_gen(_depth, :packed, :bool), do: SD.list_of(SD.boolean())
70✔
235
  defp get_gen(_depth, :unpacked, :bool), do: SD.list_of(SD.boolean())
50✔
236

237
  defp get_gen(_depth, :packed, :int32), do: SD.list_of(SD.integer())
70✔
238
  defp get_gen(_depth, :packed, :int64), do: SD.list_of(SD.integer())
70✔
239
  defp get_gen(_depth, :packed, :sint32), do: SD.list_of(SD.integer())
70✔
240
  defp get_gen(_depth, :packed, :sint64), do: SD.list_of(SD.integer())
70✔
241
  defp get_gen(_depth, :packed, :sfixed32), do: SD.list_of(SD.integer())
70✔
242
  defp get_gen(_depth, :packed, :sfixed64), do: SD.list_of(SD.integer())
70✔
243
  defp get_gen(_depth, :packed, :fixed32), do: SD.list_of(SD.integer(0..((1 <<< 32) - 1)))
70✔
244
  defp get_gen(_depth, :packed, :fixed64), do: SD.list_of(SD.integer(0..((1 <<< 64) - 1)))
70✔
245
  defp get_gen(_depth, :unpacked, :int32), do: SD.list_of(SD.integer())
50✔
246
  defp get_gen(_depth, :unpacked, :int64), do: SD.list_of(SD.integer())
50✔
247
  defp get_gen(_depth, :unpacked, :sint32), do: SD.list_of(SD.integer())
50✔
248
  defp get_gen(_depth, :unpacked, :sint64), do: SD.list_of(SD.integer())
50✔
249
  defp get_gen(_depth, :unpacked, :sfixed32), do: SD.list_of(SD.integer())
50✔
250
  defp get_gen(_depth, :unpacked, :sfixed64), do: SD.list_of(SD.integer())
50✔
251
  defp get_gen(_depth, :unpacked, :fixed32), do: SD.list_of(SD.integer(0..((1 <<< 32) - 1)))
50✔
252
  defp get_gen(_depth, :unpacked, :fixed64), do: SD.list_of(SD.integer(0..((1 <<< 64) - 1)))
50✔
253

254
  defp get_gen(_depth, :packed, :uint32), do: SD.list_of(SD.integer(0..((1 <<< 32) - 1)))
70✔
255
  defp get_gen(_depth, :packed, :uint64), do: SD.list_of(SD.integer(0..((1 <<< 64) - 1)))
70✔
256
  defp get_gen(_depth, :unpacked, :uint32), do: SD.list_of(SD.integer(0..((1 <<< 32) - 1)))
50✔
257
  defp get_gen(_depth, :unpacked, :uint64), do: SD.list_of(SD.integer(0..((1 <<< 64) - 1)))
50✔
258

259
  defp get_gen(_depth, :packed, :float), do: SD.list_of(gen_float())
70✔
260
  defp get_gen(_depth, :packed, :double), do: SD.list_of(gen_double())
70✔
261
  defp get_gen(_depth, :unpacked, :float), do: SD.list_of(gen_float())
50✔
262
  defp get_gen(_depth, :unpacked, :double), do: SD.list_of(gen_double())
50✔
263

264
  defp get_gen(_depth, kind, {:enum, e}) when kind == :packed or kind == :unpacked do
265
    e.constants() |> Map.new() |> Map.values() |> SD.member_of() |> SD.list_of()
160✔
266
  end
267

268
  defp get_gen(_depth, :unpacked, :string), do: SD.list_of(SD.string(:printable))
120✔
269
  defp get_gen(_depth, :unpacked, :bytes), do: SD.list_of(SD.binary())
40✔
270

271
  defp get_gen(_depth, :unpacked, {:message, sub_msg}) when sub_msg in @well_known_types do
450✔
272
    []
273
  end
274

275
  defp get_gen(depth, :unpacked, {:message, sub_msg}) do
276
    SD.list_of(generate_fields_values(sub_msg, depth - 1))
110✔
277
  end
278

279
  defp get_gen(_depth, :map, {_key_ty, {:message, sub_msg}}) when sub_msg in @well_known_types do
280
    %{}
×
281
  end
282

283
  defp get_gen(depth, :map, {key_ty, {:message, sub_msg}}) do
284
    key_gen = get_gen(depth, %Scalar{default_value: :dummy}, key_ty)
80✔
285
    val_gen = generate_fields_values(sub_msg, depth - 1)
80✔
286
    map_of_for_keys(key_ty, key_gen, val_gen)
80✔
287
  end
288

289
  defp get_gen(depth, :map, {key_ty, value_ty}) do
290
    key_gen = get_gen(depth, %Scalar{default_value: :dummy}, key_ty)
680✔
291
    val_gen = get_gen(depth, %Scalar{default_value: :dummy}, value_ty)
680✔
292
    map_of_for_keys(key_ty, key_gen, val_gen)
680✔
293
  end
294

295
  defp map_of_for_keys(key_ty, key_gen, val_gen) do
296
    max_len =
760✔
297
      case key_ty do
298
        :bool -> 2
40✔
299
        {:enum, e} -> e.constants() |> Map.new() |> map_size()
×
300
        _ -> nil
720✔
301
      end
302

303
    case max_len do
760✔
304
      nil -> SD.map_of(key_gen, val_gen, max_length: 20)
720✔
305
      n -> SD.map_of(key_gen, val_gen, max_length: n)
40✔
306
    end
307
  end
308

309
  # ----------------------
310

311
  defp gen_float() do
312
    SD.one_of([
250✔
313
      SD.map(SD.integer(-10_000..10_000), &(&1 * 1.0)),
16,317✔
314
      SD.constant(:nan),
315
      SD.constant(:infinity),
316
      SD.constant(:"-infinity")
317
    ])
318
  end
319

320
  defp gen_double() do
321
    SD.one_of([
250✔
322
      # SD.map(SD.integer(-1_000_000_000..1_000_000_000), &(&1 * 1.0)),
323
      SD.float(),
324
      SD.constant(:nan),
325
      SD.constant(:infinity),
326
      SD.constant(:"-infinity")
327
    ])
328
  end
329
end
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

© 2026 Coveralls, Inc