| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one or more | |
| * contributor license agreements. See the NOTICE file distributed with | |
| * this work for additional information regarding copyright ownership. | |
| * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| * (the "License"); you may not use this file except in compliance with | |
| * the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include <log4cxx/file.h> | |
| #include "logunit.h" | |
| #include "insertwide.h" | |
| #include <log4cxx/helpers/pool.h> | |
| #include <apr_errno.h> | |
| #include <log4cxx/helpers/exception.h> | |
| #include <log4cxx/helpers/fileinputstream.h> | |
| #include <log4cxx/helpers/outputstreamwriter.h> | |
| #include <log4cxx/helpers/fileoutputstream.h> | |
| #include <log4cxx/helpers/inputstreamreader.h> | |
| #include <log4cxx/helpers/fileinputstream.h> | |
| #include <log4cxx/helpers/loglog.h> | |
| #include <log4cxx/helpers/bytebuffer.h> | |
| #include <log4cxx/helpers/transcoder.h> | |
| #if LOG4CXX_CFSTRING_API | |
| #include <CoreFoundation/CFString.h> | |
| #endif | |
| using namespace log4cxx; | |
| using namespace log4cxx::helpers; | |
| LOGUNIT_CLASS(FileTestCase) | |
| { | |
| LOGUNIT_TEST_SUITE(FileTestCase); | |
| LOGUNIT_TEST(defaultConstructor); | |
| LOGUNIT_TEST(defaultExists); | |
| LOGUNIT_TEST(defaultRead); | |
| LOGUNIT_TEST(propertyRead); | |
| LOGUNIT_TEST(propertyExists); | |
| LOGUNIT_TEST(fileWrite1); | |
| #if LOG4CXX_WCHAR_T_API | |
| LOGUNIT_TEST(wcharConstructor); | |
| #endif | |
| #if LOG4CXX_CFSTRING_API | |
| LOGUNIT_TEST(cfstringConstructor); | |
| #endif | |
| LOGUNIT_TEST(copyConstructor); | |
| LOGUNIT_TEST(assignment); | |
| LOGUNIT_TEST(deleteBackslashedFileName); | |
| LOGUNIT_TEST(testSplitMultibyteUtf8); | |
| LOGUNIT_TEST(testInvalidUtf8); | |
| LOGUNIT_TEST_SUITE_END(); | |
| #ifdef _DEBUG | |
| struct Fixture | |
| { | |
| Fixture() { | |
| helpers::LogLog::setInternalDebugging(true); | |
| } | |
| } suiteFixture; | |
| #endif | |
| public: | |
| void defaultConstructor() | |
| { | |
| File defFile; | |
| LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR(""), defFile.getPath()); | |
| } | |
| void defaultExists() | |
| { | |
| File defFile; | |
| bool exists = defFile.exists(); | |
| LOGUNIT_ASSERT_EQUAL(false, exists); | |
| } | |
| // check default constructor. read() throws an exception | |
| // if no file name was given. | |
| void defaultRead() | |
| { | |
| File defFile; | |
| try | |
| { | |
| InputStreamPtr defInput = FileInputStreamPtr(new FileInputStream(defFile)); | |
| InputStreamReaderPtr inputReader = InputStreamReaderPtr(new InputStreamReader(defInput)); | |
| LogString contents(inputReader->read()); | |
| LOGUNIT_ASSERT(false); | |
| } | |
| catch (IOException& ex) | |
| { | |
| LOG4CXX_DECODE_CHAR(msg, ex.what()); | |
| LogLog::debug(msg); | |
| } | |
| } | |
| #if LOG4CXX_WCHAR_T_API | |
| void wcharConstructor() | |
| { | |
| File propFile(L"input/patternLayout1.properties"); | |
| bool exists = propFile.exists(); | |
| LOGUNIT_ASSERT_EQUAL(true, exists); | |
| } | |
| #endif | |
| #if LOG4CXX_CFSTRING_API | |
| void cfstringConstructor() | |
| { | |
| File propFile(CFSTR("input/patternLayout1.properties")); | |
| bool exists = propFile.exists(); | |
| LOGUNIT_ASSERT_EQUAL(true, exists); | |
| } | |
| #endif | |
| void copyConstructor() | |
| { | |
| File propFile("input/patternLayout1.properties"); | |
| File copy(propFile); | |
| bool exists = copy.exists(); | |
| LOGUNIT_ASSERT_EQUAL(true, exists); | |
| } | |
| void assignment() | |
| { | |
| File propFile("input/patternLayout1.properties"); | |
| File copy = propFile; | |
| bool exists = copy.exists(); | |
| LOGUNIT_ASSERT_EQUAL(true, exists); | |
| } | |
| void propertyRead() | |
| { | |
| File propFile("input/patternLayout1.properties"); | |
| InputStreamPtr propStream = FileInputStreamPtr(new FileInputStream(propFile)); | |
| InputStreamReaderPtr propReader = InputStreamReaderPtr(new InputStreamReader(propStream)); | |
| LogString props(propReader->read()); | |
| LogString line1(LOG4CXX_STR("# Licensed to the Apache Software Foundation (ASF) under one or more")); | |
| LOGUNIT_ASSERT_EQUAL(line1, props.substr(0, line1.length())); | |
| } | |
| void propertyExists() | |
| { | |
| File propFile("input/patternLayout1.properties"); | |
| bool exists = propFile.exists(); | |
| LOGUNIT_ASSERT_EQUAL(true, exists); | |
| } | |
| void fileWrite1() | |
| { | |
| OutputStreamPtr fos = FileOutputStreamPtr( | |
| new FileOutputStream(LOG4CXX_STR("output/fileWrite1.txt"))); | |
| OutputStreamWriterPtr osw = OutputStreamWriterPtr(new OutputStreamWriter(fos)); | |
| LogString greeting(LOG4CXX_STR("Hello, World")); | |
| greeting.append(LOG4CXX_EOL); | |
| osw->write(greeting); | |
| InputStreamPtr is = FileInputStreamPtr( | |
| new FileInputStream(LOG4CXX_STR("output/fileWrite1.txt"))); | |
| InputStreamReaderPtr isr = InputStreamReaderPtr(new InputStreamReader(is)); | |
| LogString reply = isr->read(); | |
| LOGUNIT_ASSERT_EQUAL(greeting, reply); | |
| } | |
| /** | |
| * Tests conversion of backslash containing file names. | |
| * Would cause infinite loop due to bug LOGCXX-105. | |
| */ | |
| void deleteBackslashedFileName() | |
| { | |
| File file("output\\bogus.txt"); | |
| file.deleteFile(); | |
| } | |
| class MockInputStream : public InputStream | |
| { | |
| ByteBuffer m_data; | |
| public: | |
| MockInputStream(const char* data, size_t charCount) | |
| : m_data(const_cast<char*>(data), charCount) | |
| {} | |
| int read(ByteBuffer& dst) override | |
| { | |
| auto availableBytes = m_data.remaining(); | |
| if (availableBytes < 1) | |
| return -1; | |
| int count = 0; | |
| for (auto p = m_data.current(); count < availableBytes && dst.put(*p); ++p) | |
| ++count; | |
| m_data.increment_position(count); | |
| return count; | |
| } | |
| void close() override {} | |
| }; | |
| /** | |
| * Tests behavior when a multibyte UTF-8 sequence occurs on a read boundary | |
| */ | |
| void testSplitMultibyteUtf8() | |
| { | |
| // InputStreamReader uses a buffer of size 4096 | |
| std::string input( 4094, 'A' ); | |
| // räksmörgås.josefsson.org | |
| input.append("\162\303\244\153\163\155\303\266\162\147\303\245\163\056\152\157\163\145\146\163\163\157\156\056\157\162\147"); | |
| InputStreamReader reader(std::make_shared<MockInputStream>(input.c_str(), input.size()), CharsetDecoder::getUTF8Decoder()); | |
| auto contentLS = reader.read(); | |
| LOG4CXX_ENCODE_CHAR(content, contentLS); | |
| LOGUNIT_ASSERT_EQUAL(input, content); | |
| } | |
| /** | |
| * Tests behavior given an incomplete multibyte UTF-8 sequence in the input | |
| */ | |
| void testInvalidUtf8() | |
| { | |
| // 0xC2 is a generic start byte for a 2-byte sequence in UTF-8. | |
| char input[] = { 'A', (char)0xC2, 'B', 'C', 0 }; | |
| InputStreamReader reader(std::make_shared<MockInputStream>(input, 4), CharsetDecoder::getUTF8Decoder()); | |
| try | |
| { | |
| reader.read(); | |
| LOGUNIT_ASSERT(false); | |
| } | |
| catch (const Exception& ex) | |
| { | |
| LOG4CXX_DECODE_CHAR(msg, ex.what()); | |
| LogLog::debug(msg); | |
| } | |
| } | |
| }; | |
| LOGUNIT_TEST_SUITE_REGISTRATION(FileTestCase); |