Coverage Report

Created: 2026-05-16 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/log4cplus/src/property.cxx
Line
Count
Source
1
// Module:  Log4CPLUS
2
// File:    property.cxx
3
// Created: 2/2002
4
// Author:  Tad E. Smith
5
//
6
//
7
// Copyright 2002-2017 Tad E. Smith
8
//
9
// Licensed under the Apache License, Version 2.0 (the "License");
10
// you may not use this file except in compliance with the License.
11
// You may obtain a copy of the License at
12
//
13
//     http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software
16
// distributed under the License is distributed on an "AS IS" BASIS,
17
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
// See the License for the specific language governing permissions and
19
// limitations under the License.
20
21
#include <log4cplus/config.hxx>
22
23
#include <cstring>
24
#if defined (UNICODE)
25
#  include <cwctype>
26
#else
27
#  include <cctype>
28
#endif
29
#if defined (LOG4CPLUS_HAVE_CODECVT_UTF8_FACET) \
30
    || defined (LOG4CPLUS_HAVE_CODECVT_UTF16_FACET) \
31
    || defined (LOG4CPLUS_HAVE_CODECVT_UTF32_FACET)
32
#  include <codecvt>
33
#endif
34
#include <locale>
35
#include <fstream>
36
#include <sstream>
37
#include <iostream>
38
#include <log4cplus/streams.h>
39
#include <log4cplus/fstreams.h>
40
#include <log4cplus/helpers/stringhelper.h>
41
#include <log4cplus/helpers/property.h>
42
#include <log4cplus/internal/internal.h>
43
#include <log4cplus/internal/env.h>
44
#include <log4cplus/helpers/loglog.h>
45
#include <log4cplus/exception.h>
46
#include <log4cplus/configurator.h>
47
48
#if defined (LOG4CPLUS_WITH_UNIT_TESTS)
49
#include <catch.hpp>
50
#endif
51
52
53
namespace log4cplus { namespace helpers {
54
55
56
const tchar Properties::PROPERTIES_COMMENT_CHAR = LOG4CPLUS_TEXT('#');
57
58
59
namespace
60
{
61
62
63
static
64
int
65
is_space (tchar ch)
66
0
{
67
#if defined (UNICODE)
68
    return std::iswspace (ch);
69
#else
70
0
    return std::isspace (static_cast<unsigned char>(ch));
71
0
#endif
72
0
}
73
74
75
static
76
void
77
trim_leading_ws (tstring & str)
78
0
{
79
0
    tstring::iterator it = str.begin ();
80
0
    for (; it != str.end (); ++it)
81
0
    {
82
0
        if (! is_space (*it))
83
0
            break;
84
0
    }
85
0
    str.erase (str.begin (), it);
86
0
}
87
88
89
static
90
void
91
trim_trailing_ws (tstring & str)
92
0
{
93
0
    tstring::reverse_iterator rit = str.rbegin ();
94
0
    for (; rit != str.rend (); ++rit)
95
0
    {
96
0
        if (! is_space (*rit))
97
0
            break;
98
0
    }
99
0
    str.erase (rit.base (), str.end ());
100
0
}
101
102
103
static
104
void
105
trim_ws (tstring & str)
106
0
{
107
0
    trim_trailing_ws (str);
108
0
    trim_leading_ws (str);
109
0
}
110
111
112
#if defined (LOG4CPLUS_WITH_UNIT_TESTS)
113
CATCH_TEST_CASE( "String trimming", "[strings][properties]")
114
{
115
    CATCH_SECTION ("trim trailing whitespace")
116
    {
117
        tstring trailing_ws (LOG4CPLUS_TEXT ("abcd \t\n\v\f\r"));
118
        trim_trailing_ws (trailing_ws);
119
        CATCH_REQUIRE (trailing_ws == LOG4CPLUS_TEXT ("abcd"));
120
    }
121
122
    CATCH_SECTION ("trim leading whitespace")
123
    {
124
        tstring leading_ws (LOG4CPLUS_TEXT (" \t\n\v\f\rabcd"));
125
        trim_leading_ws (leading_ws);
126
        CATCH_REQUIRE (leading_ws == LOG4CPLUS_TEXT ("abcd"));
127
    }
128
129
    CATCH_SECTION ("trim all whitespace")
130
    {
131
        tstring ws (LOG4CPLUS_TEXT (" \t\n\v\f\rabcd \t\n\v\f\r"));
132
        trim_ws (ws);
133
        CATCH_REQUIRE (ws == LOG4CPLUS_TEXT ("abcd"));
134
    }
135
}
136
#endif
137
138
139
void
140
imbue_file_from_flags (tistream & file, unsigned flags)
141
0
{
142
0
    switch (flags & (Properties::fEncodingMask << Properties::fEncodingShift))
143
0
    {
144
#if defined (LOG4CPLUS_HAVE_CODECVT_UTF8_FACET) && defined (UNICODE)
145
    case Properties::fUTF8:
146
        file.imbue (
147
            std::locale (file.getloc (),
148
                new std::codecvt_utf8<tchar, 0x10FFFF,
149
                    static_cast<std::codecvt_mode>(std::consume_header | std::little_endian)>));
150
        break;
151
#endif
152
153
#if defined (LOG4CPLUS_HAVE_CODECVT_UTF16_FACET) && defined (UNICODE)
154
    case Properties::fUTF16:
155
        file.imbue (
156
            std::locale (file.getloc (),
157
                new std::codecvt_utf16<tchar, 0x10FFFF,
158
                    static_cast<std::codecvt_mode>(std::consume_header | std::little_endian)>));
159
        break;
160
161
#elif defined (UNICODE) && defined (WIN32)
162
    case Properties::fUTF16:
163
        file.imbue (
164
            std::locale (file.getloc (),
165
                // TODO: This should actually be a custom "null" facet
166
                // that just copies the chars to wchar_t buffer.
167
                new std::codecvt<wchar_t, char, std::mbstate_t>));
168
        break;
169
170
#endif
171
172
#if defined (LOG4CPLUS_HAVE_CODECVT_UTF32_FACET) && defined (UNICODE)
173
    case Properties::fUTF32:
174
        file.imbue (
175
            std::locale (file.getloc (),
176
                new std::codecvt_utf32<tchar, 0x10FFFF,
177
                    static_cast<std::codecvt_mode>(std::consume_header | std::little_endian)>));
178
        break;
179
#endif
180
181
0
    case Properties::fUnspecEncoding:;
182
0
    default:
183
        // Do nothing.
184
0
        ;
185
0
    }
186
0
}
187
188
189
} // namespace
190
191
192
/**
193
 * Perform variable substitution in string <code>val</code> from
194
 * environment variables.
195
 *
196
 * <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
197
 *
198
 * <p>For example, if the System properties contains "key=value", then
199
 * the call
200
 * <pre>
201
 * string s;
202
 * substEnvironVars(s, "Value of key is ${key}.");
203
 * </pre>
204
 *
205
 * will set the variable <code>s</code> to "Value of key is value.".
206
 *
207
 * <p>If no value could be found for the specified key, then
208
 * substitution defaults to the empty string.
209
 *
210
 * <p>For example, if there is no environment variable "inexistentKey",
211
 * then the call
212
 *
213
 * <pre>
214
 * string s;
215
 * substEnvironVars(s, "Value of inexistentKey is [${inexistentKey}]");
216
 * </pre>
217
 * will set <code>s</code> to "Value of inexistentKey is []"
218
 *
219
 * @param val The string on which variable substitution is performed.
220
 * @param dest The result.
221
 */
222
bool
223
substVars (tstring & dest, const tstring & val,
224
    helpers::Properties const & props, helpers::LogLog& loglog,
225
    unsigned flags)
226
0
{
227
0
    static tchar const DELIM_START[] = LOG4CPLUS_TEXT("${");
228
0
    static tchar const DELIM_STOP[] = LOG4CPLUS_TEXT("}");
229
0
    static std::size_t const DELIM_START_LEN = 2;
230
0
    static std::size_t const DELIM_STOP_LEN = 1;
231
232
0
    tstring::size_type i = 0;
233
0
    tstring::size_type var_start, var_end;
234
0
    tstring pattern (val);
235
0
    tstring key;
236
0
    tstring replacement;
237
0
    bool changed = false;
238
0
    bool const empty_vars
239
0
        = !! (flags & PropertyConfigurator::fAllowEmptyVars);
240
0
    bool const shadow_env
241
0
        = !! (flags & PropertyConfigurator::fShadowEnvironment);
242
0
    bool const rec_exp
243
0
        = !! (flags & PropertyConfigurator::fRecursiveExpansion);
244
245
0
    while (true)
246
0
    {
247
        // Find opening paren of variable substitution.
248
0
        var_start = pattern.find(DELIM_START, i);
249
0
        if (var_start == tstring::npos)
250
0
        {
251
0
            dest = pattern;
252
0
            return changed;
253
0
        }
254
255
        // Find closing paren of variable substitution.
256
0
        var_end = pattern.find(DELIM_STOP, var_start);
257
0
        if (var_end == tstring::npos)
258
0
        {
259
0
            tostringstream buffer;
260
0
            buffer << '"' << pattern
261
0
                    << "\" has no closing brace. "
262
0
                    << "Opening brace at position " << var_start << ".";
263
0
            loglog.error(buffer.str());
264
0
            dest = val;
265
0
            return false;
266
0
        }
267
268
0
        key.assign (pattern, var_start + DELIM_START_LEN,
269
0
            var_end - (var_start + DELIM_START_LEN));
270
0
        replacement.clear ();
271
0
        if (shadow_env)
272
0
            replacement = props.getProperty (key);
273
0
        if (! shadow_env || (! empty_vars && replacement.empty ()))
274
0
            internal::get_env_var (replacement, key);
275
276
0
        if (empty_vars || ! replacement.empty ())
277
0
        {
278
            // Substitute the variable with its value in place.
279
0
            pattern.replace (var_start, var_end - var_start + DELIM_STOP_LEN,
280
0
                replacement);
281
0
            changed = true;
282
0
            if (rec_exp)
283
                // Retry expansion on the same spot.
284
0
                continue;
285
0
            else
286
                // Move beyond the just substituted part.
287
0
                i = var_start + replacement.size ();
288
0
        }
289
0
        else
290
            // Nothing has been subtituted, just move beyond the
291
            // unexpanded variable.
292
0
            i = var_end + DELIM_STOP_LEN;
293
0
    } // end while loop
294
295
0
} // end substVars()
296
297
298
///////////////////////////////////////////////////////////////////////////////
299
// Properties ctors and dtor
300
///////////////////////////////////////////////////////////////////////////////
301
302
Properties::Properties()
303
797k
    : flags (0)
304
797k
{
305
797k
}
306
307
308
309
Properties::Properties(tistream& input)
310
0
    : flags (0)
311
0
{
312
0
    init(input);
313
0
}
314
315
316
317
Properties::Properties(const tstring& inputFile, unsigned f)
318
122k
    : flags (f)
319
122k
{
320
122k
    if (inputFile.empty ())
321
122k
        return;
322
323
0
    tifstream file;
324
0
    imbue_file_from_flags (file, flags);
325
326
0
    file.open(LOG4CPLUS_FSTREAM_PREFERED_FILE_NAME(inputFile).c_str(),
327
0
        std::ios::binary);
328
0
    if (! file.good ())
329
0
        helpers::getLogLog ().error (LOG4CPLUS_TEXT ("could not open file ")
330
0
            + inputFile, (flags & Properties::fThrow) != 0);
331
332
0
    init(file);
333
0
}
334
335
336
337
void
338
Properties::init(tistream& input)
339
0
{
340
0
    if (! input)
341
0
        return;
342
343
0
    tstring buffer;
344
0
    while (std::getline (input, buffer))
345
0
    {
346
0
        trim_leading_ws (buffer);
347
348
0
        tstring::size_type const buffLen = buffer.size ();
349
0
        if (buffLen == 0 || buffer[0] == PROPERTIES_COMMENT_CHAR)
350
0
            continue;
351
352
        // Check if we have a trailing \r because we are
353
        // reading a properties file produced on Windows.
354
0
        if (buffer[buffLen-1] == LOG4CPLUS_TEXT('\r'))
355
            // Remove trailing 'Windows' \r.
356
0
            buffer.resize (buffLen - 1);
357
358
0
        if (buffer.size () >= 7 + 1 + 1
359
0
            && buffer.compare (0, 7, LOG4CPLUS_TEXT ("include")) == 0
360
0
            && is_space (buffer[7]))
361
0
        {
362
0
            tstring included (buffer, 8) ;
363
0
            trim_ws (included);
364
365
0
            tstring subIncluded;
366
0
            helpers::substVars(subIncluded, included, *this, helpers::getLogLog(), 0);
367
368
0
            tifstream file;
369
0
            imbue_file_from_flags (file, flags);
370
371
0
            file.open (LOG4CPLUS_FSTREAM_PREFERED_FILE_NAME(subIncluded).c_str(),
372
0
                std::ios::binary);
373
0
            if (! file.good ())
374
0
                helpers::getLogLog ().error (
375
0
                    LOG4CPLUS_TEXT ("could not open file ") + subIncluded);
376
377
0
            init (file);
378
0
        }
379
0
        else
380
0
        {
381
0
            tstring::size_type const idx = buffer.find(LOG4CPLUS_TEXT ('='));
382
0
            if (idx != tstring::npos)
383
0
            {
384
0
                tstring key = buffer.substr(0, idx);
385
0
                tstring value = buffer.substr(idx + 1);
386
0
                trim_trailing_ws (key);
387
0
                trim_ws (value);
388
0
                setProperty(key, value);
389
0
            }
390
0
        }
391
0
    }
392
0
}
393
394
395
396
Properties::~Properties()
397
920k
{
398
920k
}
399
400
401
402
///////////////////////////////////////////////////////////////////////////////
403
// helpers::Properties public methods
404
///////////////////////////////////////////////////////////////////////////////
405
406
407
bool
408
Properties::exists(const log4cplus::tstring& key) const
409
1.35M
{
410
1.35M
    return data.find(key) != data.end();
411
1.35M
}
412
413
414
bool
415
Properties::exists(tchar const * key) const
416
368k
{
417
368k
    return data.find(key) != data.end();
418
368k
}
419
420
421
tstring const &
422
Properties::getProperty(const tstring& key) const
423
613k
{
424
613k
    return get_property_worker (key);
425
613k
}
426
427
428
log4cplus::tstring const &
429
Properties::getProperty(tchar const * key) const
430
122k
{
431
122k
    return get_property_worker (key);
432
122k
}
433
434
435
tstring
436
Properties::getProperty(const tstring& key, const tstring& defaultVal) const
437
0
{
438
0
    StringMap::const_iterator it (data.find (key));
439
0
    if (it == data.end ())
440
0
        return defaultVal;
441
0
    else
442
0
        return it->second;
443
0
}
444
445
446
std::vector<tstring>
447
Properties::propertyNames() const
448
1.22M
{
449
1.22M
    std::vector<tstring> tmp;
450
1.22M
    tmp.reserve (data.size ());
451
1.22M
    for (auto const & kv : data)
452
1.71M
        tmp.push_back(kv.first);
453
454
1.22M
    return tmp;
455
1.22M
}
456
457
458
459
void
460
Properties::setProperty(const log4cplus::tstring& key,
461
    const log4cplus::tstring& value)
462
797k
{
463
797k
    data[key] = value;
464
797k
}
465
466
467
bool
468
Properties::removeProperty(const log4cplus::tstring& key)
469
0
{
470
0
    return data.erase(key) > 0;
471
0
}
472
473
474
Properties
475
Properties::getPropertySubset(const log4cplus::tstring& prefix) const
476
736k
{
477
736k
    Properties ret;
478
736k
    auto const prefix_len = prefix.size ();
479
736k
    std::vector<tstring> keys = propertyNames();
480
736k
    for (tstring const & key : keys)
481
1.47M
    {
482
1.47M
        int result = key.compare (0, prefix_len, prefix);
483
1.47M
        if (result == 0)
484
368k
            ret.setProperty (key.substr (prefix_len), getProperty(key));
485
1.47M
    }
486
487
736k
    return ret;
488
736k
}
489
490
491
bool
492
Properties::getInt (int & val, log4cplus::tstring const & key) const
493
0
{
494
0
    return get_type_val_worker (val, key);
495
0
}
496
497
498
bool
499
Properties::getUInt (unsigned & val, log4cplus::tstring const & key) const
500
245k
{
501
245k
    return get_type_val_worker (val, key);
502
245k
}
503
504
505
bool
506
Properties::getLong (long & val, log4cplus::tstring const & key) const
507
0
{
508
0
    return get_type_val_worker (val, key);
509
0
}
510
511
512
bool
513
Properties::getULong (unsigned long & val, log4cplus::tstring const & key) const
514
0
{
515
0
    return get_type_val_worker (val, key);
516
0
}
517
518
519
bool
520
Properties::getBool (bool & val, log4cplus::tstring const & key) const
521
982k
{
522
982k
    if (! exists (key))
523
859k
        return false;
524
525
122k
    log4cplus::tstring const & prop_val = getProperty (key);
526
122k
    return internal::parse_bool (val, prop_val);
527
982k
}
528
529
530
bool
531
Properties::getString (log4cplus::tstring & val, log4cplus::tstring const & key)
532
    const
533
122k
{
534
122k
    StringMap::const_iterator it (data.find (key));
535
122k
    if (it == data.end ())
536
122k
        return false;
537
538
0
    val = it->second;
539
0
    return true;
540
122k
}
541
542
543
template <typename StringType>
544
log4cplus::tstring const &
545
Properties::get_property_worker (StringType const & key) const
546
736k
{
547
736k
    StringMap::const_iterator it (data.find (key));
548
736k
    if (it == data.end ())
549
0
        return log4cplus::internal::empty_str;
550
736k
    else
551
736k
        return it->second;
552
736k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const& log4cplus::helpers::Properties::get_property_worker<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) const
Line
Count
Source
546
613k
{
547
613k
    StringMap::const_iterator it (data.find (key));
548
613k
    if (it == data.end ())
549
0
        return log4cplus::internal::empty_str;
550
613k
    else
551
613k
        return it->second;
552
613k
}
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const& log4cplus::helpers::Properties::get_property_worker<char const*>(char const* const&) const
Line
Count
Source
546
122k
{
547
122k
    StringMap::const_iterator it (data.find (key));
548
122k
    if (it == data.end ())
549
0
        return log4cplus::internal::empty_str;
550
122k
    else
551
122k
        return it->second;
552
122k
}
553
554
555
template <typename ValType>
556
bool
557
Properties::get_type_val_worker (ValType & val, log4cplus::tstring const & key)
558
    const
559
245k
{
560
245k
    if (! exists (key))
561
245k
        return false;
562
563
0
    log4cplus::tstring const & prop_val = getProperty (key);
564
0
    log4cplus::tistringstream iss (prop_val);
565
0
    ValType tmp_val;
566
0
    tchar ch;
567
568
0
    iss >> tmp_val;
569
0
    if (! iss)
570
0
        return false;
571
0
    iss >> ch;
572
0
    if (iss)
573
0
        return false;
574
575
0
    val = tmp_val;
576
0
    return true;
577
0
}
Unexecuted instantiation: bool log4cplus::helpers::Properties::get_type_val_worker<int>(int&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) const
bool log4cplus::helpers::Properties::get_type_val_worker<unsigned int>(unsigned int&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) const
Line
Count
Source
559
245k
{
560
245k
    if (! exists (key))
561
245k
        return false;
562
563
0
    log4cplus::tstring const & prop_val = getProperty (key);
564
0
    log4cplus::tistringstream iss (prop_val);
565
0
    ValType tmp_val;
566
0
    tchar ch;
567
568
0
    iss >> tmp_val;
569
0
    if (! iss)
570
0
        return false;
571
0
    iss >> ch;
572
0
    if (iss)
573
0
        return false;
574
575
0
    val = tmp_val;
576
0
    return true;
577
0
}
Unexecuted instantiation: bool log4cplus::helpers::Properties::get_type_val_worker<long>(long&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) const
Unexecuted instantiation: bool log4cplus::helpers::Properties::get_type_val_worker<unsigned long>(unsigned long&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) const
578
579
580
#if defined (LOG4CPLUS_WITH_UNIT_TESTS)
581
CATCH_TEST_CASE ("Properties", "[properties]")
582
{
583
    static tchar const PROP_ABC[] = LOG4CPLUS_TEXT ("a.b.c");
584
    Properties props;
585
586
    CATCH_SECTION ("new object is empty")
587
    {
588
        CATCH_REQUIRE (props.size () == 0);
589
    }
590
591
    CATCH_SECTION ("added property can be retrieved")
592
    {
593
        props.setProperty (PROP_ABC, LOG4CPLUS_TEXT ("true"));
594
        CATCH_REQUIRE (props.exists (PROP_ABC));
595
        CATCH_REQUIRE (props.exists (LOG4CPLUS_C_STR_TO_TSTRING (PROP_ABC)));
596
        CATCH_REQUIRE (props.getProperty (PROP_ABC)
597
            == LOG4CPLUS_TEXT ("true"));
598
    }
599
600
    CATCH_SECTION ("type conversions work")
601
    {
602
        bool bool_;
603
        int int_;
604
        unsigned int uint;
605
        long long_;
606
        unsigned long ulong;
607
608
        tistringstream iss (
609
            LOG4CPLUS_TEXT ("bool=true\r\n")
610
            LOG4CPLUS_TEXT ("bool1=1\n")
611
            LOG4CPLUS_TEXT ("int=-1\n")
612
            LOG4CPLUS_TEXT ("uint=42\n")
613
            LOG4CPLUS_TEXT ("long=-65537\n")
614
            LOG4CPLUS_TEXT ("ulong=65537")
615
        );
616
        Properties from_stream (iss);
617
618
        CATCH_REQUIRE (from_stream.getBool (bool_, LOG4CPLUS_TEXT ("bool")));
619
        CATCH_REQUIRE (bool_);
620
        CATCH_REQUIRE (from_stream.getBool (bool_, LOG4CPLUS_TEXT ("bool1")));
621
        CATCH_REQUIRE (bool_);
622
        CATCH_REQUIRE (from_stream.getInt (int_, LOG4CPLUS_TEXT ("int")));
623
        CATCH_REQUIRE (int_ == -1);
624
        CATCH_REQUIRE (from_stream.getUInt (uint, LOG4CPLUS_TEXT ("uint")));
625
        CATCH_REQUIRE (uint == 42);
626
        CATCH_REQUIRE (from_stream.getLong (long_, LOG4CPLUS_TEXT ("long")));
627
        CATCH_REQUIRE (long_ == -65537);
628
        CATCH_REQUIRE (from_stream.getULong (ulong, LOG4CPLUS_TEXT ("ulong")));
629
        CATCH_REQUIRE (ulong == 65537);
630
    }
631
632
    CATCH_SECTION ("remove property")
633
    {
634
        props.setProperty (PROP_ABC, LOG4CPLUS_TEXT ("true"));
635
        CATCH_REQUIRE (props.exists (PROP_ABC));
636
        props.removeProperty (PROP_ABC);
637
        CATCH_REQUIRE (! props.exists (PROP_ABC));
638
    }
639
640
    CATCH_SECTION ("retrieve property names")
641
    {
642
        props.setProperty (PROP_ABC, LOG4CPLUS_TEXT ("true"));
643
        static tchar const PROP_SECOND[] = LOG4CPLUS_TEXT ("second");
644
        props.setProperty (LOG4CPLUS_TEXT ("second"), LOG4CPLUS_TEXT ("false"));
645
        std::vector<log4cplus::tstring> names (props.propertyNames ());
646
        CATCH_REQUIRE (std::find (std::begin (names), std::end (names),
647
                PROP_ABC) != std::end (names));
648
        CATCH_REQUIRE (std::find (std::begin (names), std::end (names),
649
                PROP_SECOND) != std::end (names));
650
    }
651
652
    CATCH_SECTION ("throw on nonexistent file")
653
    {
654
        auto && f = [] {
655
            Properties props (LOG4CPLUS_TEXT ("xxx does not exist"), Properties::fThrow);
656
        };
657
        CATCH_REQUIRE_THROWS_AS (f (), log4cplus::exception);
658
    }
659
}
660
#endif
661
662
663
} } // namespace log4cplus { namespace helpers {