File: string_test_m.F90

package info (click to toggle)
fortran-julienne 3.6.2-4
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 828 kB
  • sloc: f90: 5,043; makefile: 22; ansic: 14
file content (466 lines) | stat: -rw-r--r-- 22,460 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
! Copyright (c) 2024-2025, The Regents of the University of California and Sourcery Institute
! Terms of use are as specified in LICENSE.txt

#include "language-support.F90"

module string_test_m
  use assert_m, only : assert
  use iso_c_binding, only : c_bool, c_size_t

  use julienne_m, only : &
     test_t &
    ,test_result_t &
    ,test_description_t &
    ,test_diagnosis_t &
    ,usher &
    ,string_t &
    ,operator(.all.) &
    ,operator(.also.) &
    ,operator(.approximates.) &
    ,operator(.cat.) &
    ,operator(.csv.) &
    ,operator(.equalsExpected.) &
    ,operator(.sv.) &
    ,operator(.within.)

  implicit none

  private
  public :: string_test_t

  type, extends(test_t) :: string_test_t
  contains
    procedure, nopass :: subject
    procedure, nopass :: results
  end type

contains

  pure function subject() result(specimen)
    character(len=:), allocatable :: specimen
    specimen = "The string_t type"
  end function

  function results() result(test_results)
    type(test_result_t), allocatable :: test_results(:)
    type(test_description_t), allocatable :: test_descriptions(:)
    type(string_test_t) string_test

    test_descriptions = [ &
       test_description_t("is_allocated() result .true. if & only if the string_t component(s) is/are allocated", usher(check_allocation))&
      ,test_description_t("extracting a key string from a colon-separated key/value pair", usher(                           extracts_key))&
      ,test_description_t("extracting double-precision value from colon-separated key/value pair", usher(extracts_double_precision_value))&
      ,test_description_t("extracting a real value from a colon-separated key/value pair", usher(                    extracts_real_value))&
      ,test_description_t("extracting a character value from a colon-separated key/value pair", usher(          extracts_character_value))&
      ,test_description_t("extracting a string value from a colon-separated key/value pair", usher(                extracts_string_value))&
      ,test_description_t("extracting an integer value from a colon-separated key/value pair", usher(             extracts_integer_value))&
      ,test_description_t("extracting a logical value from a colon-separated key/value pair", usher(              extracts_logical_value))&
      ,test_description_t("extracting an integer array value from a colon-separated key/value pair", usher( extracts_integer_array_value))&
      ,test_description_t("extracting an real array value from a colon-separated key/value pair", usher(       extracts_real_array_value))&
      ,test_description_t("extracting a double-precision array from a colon-separated key/value pair", usher(    extracts_dp_array_value))&
      ,test_description_t('supporting operator(==) for string_t and character operands', usher(            supports_equivalence_operator))&
      ,test_description_t('supporting operator(/=) for string_t and character operands', usher(        supports_non_equivalence_operator))&
      ,test_description_t('assigning a string_t object to a character variable', usher(                    assigns_string_t_to_character))&
      ,test_description_t('assigning a character variable to a string_t object', usher(                    assigns_character_to_string_t))&
      ,test_description_t('supporting operator(//) for string_t and character operands', usher(          supports_concatenation_operator))&
      ,test_description_t('constructing from a default integer and an integer(c_size_t)', usher(                constructs_from_integers))&
      ,test_description_t('constructing from a default real value', usher(                                  constructs_from_default_real))&
      ,test_description_t('constructing from a double-precision value', usher(                          constructs_from_double_precision))&
      ,test_description_t('constructing from a default-precision complex value', usher(                  constructs_from_default_complex))&
      ,test_description_t('constructing from a default-kind logical value', usher(                       constructs_from_default_logical))&
      ,test_description_t('constructing from a logical(c_bool) value', usher(                             constructs_from_logical_c_bool))&
      ,test_description_t('extracting a file base name', usher(                                                  extracts_file_base_name))&
      ,test_description_t('extracting a file name extension', usher(                                        extracts_file_name_extension))&
      ,test_description_t('supporting unary operator(.cat.) for array arguments', usher(                           concatenates_elements))&
      ,test_description_t('constructing bracketed strings', usher(                                                      brackets_strings))&
      ,test_description_t("extracting a string_t array value from a colon-separated key/value pair", usher(  extracts_string_array_value))&
      ,test_description_t('constructing (comma-)separated values from character or string_t arrays', usher(  constructs_separated_values))&
      ,test_description_t('constructing from a double-precision complex value', usher(          constructs_from_double_precision_complex))&
    ]
    test_results = string_test%run(test_descriptions)
  end function

  pure function check_allocation() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    type(string_t) :: scalar_not_allocated, scalar_allocated, array_allocated(2), array_not_allocated(2)

    scalar_allocated = string_t("")
    array_allocated = [string_t("yada yada"), string_t("blah blah blah")]

    associate(not_any_allocated => .not. any([scalar_not_allocated%is_allocated(), array_not_allocated%is_allocated()]))
      associate(all_allocated => all([scalar_allocated%is_allocated(), array_allocated%is_allocated()]))
        test_diagnosis = test_diagnosis_t( &
           test_passed = not_any_allocated .and. all_allocated &
          ,diagnostics_string = "expected .true., true.; actual " // string_t(not_any_allocated) // string_t(all_allocated) &
        )
      end associate
    end associate
  end function

  function extracts_key() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(line => string_t('"foo" : "bar"'))
      test_diagnosis = line%get_json_key() .equalsExpected. "foo"
    end associate
  end function

  function extracts_double_precision_value() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    double precision, parameter :: tolerance = 1D-16

    associate(line => string_t('"pi" : 3.141592653589793D0'))
       test_diagnosis = line%get_json_value(key="pi", mold=0.D0) .approximates. 3.141592653589793D0 .within. tolerance
    end associate
  end function

  function extracts_real_value() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    real, parameter :: tolerance = 1E-08

    associate(line => string_t('"pi" : 3.14159'))
      associate(json_value => line%get_json_value(key=string_t("pi"), mold=1.))
        test_diagnosis = json_value .approximates. 3.14159 .within. tolerance
      end associate
    end associate
  end function

  function extracts_character_value() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(line => string_t('"foo" : "bar"'), line_with_comma => string_t('"foo" : "bar",'))
      test_diagnosis = (line%get_json_value(           key=         "foo" , mold="") .equalsExpected. "bar") &
                .also. (line_with_comma%get_json_value(key=         "foo" , mold="") .equalsExpected. "bar") &
                .also. (line%get_json_value(           key=string_t("foo"), mold="") .equalsExpected. "bar") &
                .also. (line_with_comma%get_json_value(key=string_t("foo"), mold="") .equalsExpected. "bar")
    end associate
  end function

  function extracts_string_value() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(line => string_t('"foo" : "bar"'), line_with_comma => string_t('"foo" : "bar",'))
      test_diagnosis = (line%get_json_value(           key=         "foo" , mold=string_t("")) .equalsExpected. "bar") &
                .also. (line_with_comma%get_json_value(key=         "foo" , mold=string_t("")) .equalsExpected. "bar") &
                .also. (line%get_json_value(           key=string_t("foo"), mold=string_t("")) .equalsExpected. "bar") &
                .also. (line_with_comma%get_json_value(key=string_t("foo"), mold=string_t("")) .equalsExpected. "bar")
    end associate
  end function

  function extracts_integer_value() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(line => string_t('"an integer" : 99'))
      associate(json_value => line%get_json_value(key=string_t("an integer"), mold=0))
        test_diagnosis = test_diagnosis_t( &
           test_passed = json_value == 99 &
          ,diagnostics_string = "expected 99, actual " // string_t(json_value) &
        )
      end associate
    end associate
  end function

  function extracts_logical_value() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate( &
      key_true_pair => string_t('"yada yada" : true'), &
      key_false_pair => string_t('"blah blah" : false'), &
      trailing_comma => string_t('"trailing comma" : true,') &
    )
      associate( &
         true => key_true_pair%get_json_value(key=string_t("yada yada"), mold=.true.) &
        ,true_too => trailing_comma%get_json_value(key=string_t("trailing comma"), mold=.true.) &
        ,false => key_false_pair%get_json_value(key=string_t("blah blah"), mold=.true.) &
      )
        test_diagnosis = test_diagnosis_t( &
           test_passed = all([true, true_too, .not. false]) &
          ,diagnostics_string = "expected T,T,T; actual  " // .csv. string_t([true, true_too, .not. false]) &
        )
      end associate
    end associate
  end function

  function extracts_string_array_value() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(key_string_array_pair => string_t('"lead singer" : ["stevie", "ray", "vaughn"],'))
      associate(string_array => key_string_array_pair%get_json_value(key="lead singer", mold=[string_t::]))
        test_diagnosis = .all. (string_array .equalsExpected. [string_t("stevie"), string_t("ray"), string_t("vaughn")])
      end associate
    end associate
  end function

  function extracts_integer_array_value() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(key_integer_array_pair => string_t('"some key" : [1, 2, 3],'))
      associate(integer_array => key_integer_array_pair%get_json_value(key=string_t("some key"), mold=[integer::]))
        test_diagnosis = .all. (integer_array .equalsExpected. [1,2,3])
      end associate
    end associate
  end function

  function extracts_real_array_value() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    real, parameter :: tolerance = 1E-08

    associate(key_real_array_pair => string_t('"a key" : [1., 2., 4.],'))
      associate(real_array => key_real_array_pair%get_json_value(key=string_t("a key"), mold=[real::]))
        test_diagnosis = test_diagnosis_t( &
           test_passed = all(abs(real_array - [1., 2., 4.]) < tolerance) &
          ,diagnostics_string = "expected 1,2,3; actual " // .csv. string_t(real_array) &
        )
      end associate
    end associate
  end function

  function extracts_dp_array_value() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    double precision, parameter :: tolerance = 1E-16

    associate(key_dp_array_pair => string_t('"a key" : [1.D0, 2.D0, 4.D0],'))
      associate(dp_array => key_dp_array_pair%get_json_value(key=string_t("a key"), mold=[double precision::]))
        test_diagnosis = test_diagnosis_t( &
           test_passed = all(abs(dp_array - [1D0, 2D0, 4D0]) < tolerance) &
          ,diagnostics_string = "expected 1.,2.,3.; actual " // .csv. string_t(dp_array) &
        )
      end associate
    end associate
  end function

  function supports_equivalence_operator() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(comparisons => [ string_t("abcdefg") == string_t("abcdefg") &
                              ,string_t("xyz pdq") ==          "xyz pdq"  &
                              ,         "123.456"  == string_t("123.456") &
                              ,         "123.456"  == string_t("123"    )])
      test_diagnosis = test_diagnosis_t( &
         test_passed = all(comparisons .eqv. [.true.,.true.,.true.,.false.]) &
        ,diagnostics_string = "expected T,T,T,F; actual " // .csv. string_t([comparisons(1:3), .not. comparisons(4)]) &
      )
    end associate
  end function

  function supports_non_equivalence_operator() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(non_equivalent_strings => [string_t("abcdefg") /= string_t("xyz pdq") &
                                        ,string_t("xyz pdq") /=          "abcdefg"  &
                                        ,         "123.456"  /= string_t("456.123") &
                                        ,         "123.456"  /= string_t("123.456")])
      test_diagnosis = test_diagnosis_t( &
         test_passed = all(non_equivalent_strings .eqv. [.true.,.true.,.true.,.false.]) &
        ,diagnostics_string = "expected T,T,T,F; actual " // .csv. string_t(non_equivalent_strings) &
      )
    end associate
  end function

  function assigns_string_t_to_character() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    character(len=:), allocatable :: lhs

    associate(rhs => string_t("ya don't say"))
      lhs = rhs
      test_diagnosis = test_diagnosis_t( &
         test_passed = lhs == rhs &
        ,diagnostics_string = "expected lhs == rhs; actual lhs = " // lhs // ", rhs = " // rhs &
      )
    end associate
  end function

  function assigns_character_to_string_t() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    character(len=*), parameter :: rhs = "well, alrighty then"
    type(string_t) lhs

    lhs = rhs
    test_diagnosis = test_diagnosis_t( &
       test_passed = lhs == rhs &
      ,diagnostics_string = "expected lhs == rhs; actual lhs = " // lhs // ", rhs = " // rhs &
    )
  end function

  function supports_concatenation_operator() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    character(len=*), parameter :: prefix = "foo", postfix="bar", expected = "foo yada yada bar"

    associate(infix => string_t(" yada yada "))
      associate(string_string_string => prefix // infix // postfix, string_character_string => prefix // infix%string() // postfix)
        test_diagnosis = test_diagnosis_t( &
           test_passed = all([string_string_string == expected, string_character_string == expected]) &
          ,diagnostics_string = "expected '"// expected // "', actual " // string_string_string // "," // string_character_string &
        )
      end associate
    end associate
  end function

  function constructs_from_integers() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    test_diagnosis = (string_t(1234567890)                   .equalsExpected. "1234567890") &
              .also. (string_t(1234567890123456789_c_size_t) .equalsExpected. "1234567890123456789")
  end function

  function constructs_from_default_real() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    real, parameter :: real_value = -1./1024. ! use a negative power of 2 for an exactly representable rational number
    real, parameter :: tolerance = 0.
    real read_value
    character(len=:), allocatable :: character_representation

    associate(string => string_t(real_value))
      character_representation = string%string()
      read(character_representation, *) read_value
      test_diagnosis = read_value .approximates. real_value .within. tolerance
    end associate
  end function

  function constructs_from_double_precision() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    double precision, parameter :: expected_value = -1D0/1024D0 ! use a negative power of 2 for an exactly representable rational number
    double precision, parameter :: tolerance = 0D0
    double precision read_value
    character(len=:), allocatable :: character_representation

    associate(string => string_t(expected_value))
      character_representation = string%string()
      read(character_representation, *) read_value
      test_diagnosis = read_value .approximates. expected_value .within. tolerance
    end associate
  end function

  function constructs_from_default_complex() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    real, parameter :: real_value = -1./1024. ! use a negative power of 2 for an exactly representable rational number
    real, parameter :: tolerance = 1E-08
    complex, parameter :: z = (real_value, real_value)
    complex read_value
    character(len=:), allocatable :: character_representation

    associate(string => string_t(z))
      character_representation = string%string()
      read(character_representation, *) read_value
      test_diagnosis = test_diagnosis_t( &
         test_passed = abs(read_value - z) < tolerance &
        ,diagnostics_string = "expected '"// string_t(z) // "', actual " // string_t(read_value) &
      )
    end associate
  end function

  function constructs_from_double_precision_complex() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    double precision, parameter :: double_precision_value = -1D0/1024D0 ! use a negative power of 2 for an exactly representable rational number
    double precision, parameter :: tolerance = 1E-16
    complex(kind(1D0)), parameter :: z = (double_precision_value, double_precision_value)
    complex(kind(1D0)) read_value
    character(len=:), allocatable :: character_representation

    associate(string => string_t(z))
      character_representation = string%string()
      read(character_representation, *) read_value
      test_diagnosis = test_diagnosis_t( &
         test_passed = abs(read_value - z) < tolerance &
        ,diagnostics_string = "expected '"// string_t(z) // "', actual " // string_t(read_value) &
      )
    end associate
  end function

  function constructs_from_default_logical() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(true => string_t(.true.), false => string_t(.false.))
      test_diagnosis = test_diagnosis_t( &
         test_passed = all([true%string() == "T", false%string() == "F"]) &
        ,diagnostics_string = "expected T, F; actual '"// true%string() // ", " // false%string() &
      )
    end associate
  end function

  function constructs_from_logical_c_bool() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(true => string_t(.true._c_bool), false => string_t(.false._c_bool))
      test_diagnosis = test_diagnosis_t( &
         test_passed = true%string() == "T" .and. false%string() == "F" &
        ,diagnostics_string = "expected T, F; actual '"// true%string() // ", " // false%string() &
      )
    end associate
  end function

  function extracts_file_base_name() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    character(len=*), parameter :: expected = "foo .bar"

    associate(string => string_t(" foo .bar.too "))
      associate(base_name => string%base_name())
        test_diagnosis = test_diagnosis_t( &
           test_passed = base_name == expected &
          ,diagnostics_string = "expected "// expected // ", actual " // base_name &
        )
      end associate
    end associate
  end function

  function extracts_file_name_extension() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(string => string_t(" foo .bar.too "))
      test_diagnosis = string%file_extension() .equalsExpected. "too"
    end associate
  end function

  function concatenates_elements() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis
    test_diagnosis = (.cat. [string_t("foo"), string_t("bar")]) .equalsExpected. "foobar"
  end function

  function brackets_strings() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    associate(scalar => string_t("do re mi"))

#if (! defined(__GFORTRAN__)) || GCC_VERSION > 150000
      associate(array  => string_t(["do", "re", "mi"]))
        test_diagnosis = test_diagnosis_t( &
          test_passed = scalar%bracket()        == string_t("[do re mi]")                                  &
               .and. all(array%bracket()        == [string_t("[do]"), string_t("[re]"), string_t("[mi]")]) &
               .and. all(array%bracket('"')     == [string_t('"do"'), string_t('"re"'), string_t('"mi"')]) &
               .and. all(array%bracket("{","}") == [string_t('{do}'), string_t('{re}'), string_t('{mi}')]) &
          ,diagnostics_string = "" &
        )
      end associate
#else
      block
        type(string_t), allocatable :: array(:)
        array = string_t(["do", "re", "mi"])
        test_diagnosis = test_diagnosis_t( &
          test_passed = scalar%bracket()        == string_t("[do re mi]")                                  &
               .and. all(array%bracket()        == [string_t("[do]"), string_t("[re]"), string_t("[mi]")]) &
               .and. all(array%bracket('"')     == [string_t('"do"'), string_t('"re"'), string_t('"mi"')]) &
               .and. all(array%bracket("{","}") == [string_t('{do}'), string_t('{re}'), string_t('{mi}')]) &
          ,diagnostics_string = "" &
        )
      end block
#endif
    end associate
  end function

  function constructs_separated_values() result(test_diagnosis)
    type(test_diagnosis_t) test_diagnosis

    test_diagnosis = test_diagnosis_t( &
      test_passed = &
              "a,bc,def" == .csv. [string_t("a"), string_t("bc"), string_t("def")]    &
        .and. "abc,def"  == .csv. ["abc", "def"]                                      &
        .and. "do|re|mi" == (string_t(["do", "re", "mi"])         .sv.          "|" ) &
        .and. "dore|mi"  == (([string_t("dore"), string_t("mi")]) .sv. string_t("|")) &
        .and. "do|re|mi" == (         ["do", "re", "mi"]          .sv.          "|" ) &
        .and. "do|re|mi" == (         ["do", "re", "mi"]          .sv. string_t("|")) &
      ,diagnostics_string = "" &
    )
  end function

end module string_test_m