Skip to content

Instantly share code, notes, and snippets.

@schwehr
Created September 5, 2015 15:18
Show Gist options
  • Save schwehr/13137d826763763fb031 to your computer and use it in GitHub Desktop.
Save schwehr/13137d826763763fb031 to your computer and use it in GitHub Desktop.
Example test file from gdal autotest2
// Test convenience functions:
// http://trac.osgeo.org/gdal/browser/trunk/gdal/port/cpl_conv.cpp
//
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed 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 <cstdio>
#include <limits>
#include "testing/base/public/gunit.h"
#include "gdal/gcore/gdal_priv.h"
#include "gdal/port/cpl_conv.h"
namespace {
TEST(CplConvTest, VerifyConfiguration) {
CPLVerifyConfiguration();
ASSERT_EQ(0, CPLGetLastErrorNo());
}
// Tests accessing and setting the GDAL runtime configuration system.
TEST(CplConvTest, GetSetConfigOption) {
// Check that 2nd argument default is returned for a non-existant key.
// Try with a nullptr default.
ASSERT_EQ(nullptr, CPLGetConfigOption("Does not exist", nullptr));
// Try with a string default.
ASSERT_STREQ("mydefault", CPLGetConfigOption("Does not exist", "mydefault"));
// Check setting and unsetting a key.
CPLSetConfigOption("TestKey", "abc123");
ASSERT_STREQ("abc123", CPLGetConfigOption("TestKey", nullptr));
CPLSetConfigOption("TestKey", nullptr);
ASSERT_EQ("A default", CPLGetConfigOption("TestKey", "A default"));
ASSERT_EQ(nullptr, CPLGetConfigOption("TestKey", nullptr));
// Test thread local without extra threads.
CPLSetThreadLocalConfigOption("ThreadLocalTestKey", "local0");
ASSERT_STREQ("local0", CPLGetConfigOption("ThreadLocalTestKey", nullptr));
CPLSetThreadLocalConfigOption("ThreadLocalTestKey", nullptr);
ASSERT_EQ(nullptr, CPLGetConfigOption("ThreadLocalTestKey", nullptr));
// TODO(schwehr): Test CPLSetThreadLocalConfigOption with two threads.
}
// Tests using the portable equivalents to malloc, calloc, and realloc.
//
// There are three cases with the CPLRealloc call:
// * nNewSize is 0: The data pointer is freed if not a nullptr.
// * Casting nNewSize to a long is negative: A good chance for a memory leak.
// Also tests an error status.
// * nNewSize is >0: Memory is allocated and data pointer contents are copied.
// The original memory is freed.
TEST(CplConvTest, Memory) {
ASSERT_EQ(nullptr, CPLMalloc(0));
ASSERT_EQ(CPLE_None, CPLGetLastErrorNo());
ASSERT_EQ(nullptr, CPLMalloc(-1));
ASSERT_EQ(CPLE_AppDefined, CPLGetLastErrorNo());
void *ptr = CPLMalloc(1);
ASSERT_NE(nullptr, ptr);
CPLFree(ptr);
ptr = CPLMalloc(999999);
ASSERT_NE(nullptr, ptr);
CPLFree(ptr);
ASSERT_EQ(nullptr, CPLCalloc(10, 0));
ASSERT_EQ(nullptr, CPLCalloc(0, 10));
char *char_ptr = static_cast<char *>(CPLCalloc(10, 1));
for (int i = 0; i < 10; i++)
ASSERT_EQ(0, char_ptr[i]);
CPLFree(char_ptr);
ASSERT_EQ(nullptr, CPLRealloc(nullptr, 0));
ptr = CPLRealloc(nullptr, 10);
ASSERT_NE(nullptr, ptr);
// Negative size values are invalid and trigger an error.
ASSERT_EQ(nullptr, CPLRealloc(nullptr, -1));
ASSERT_EQ(CPLE_AppDefined, CPLGetLastErrorNo());
// Pointer still valid.
// A realloc of a negative size will leave the ptr contents unchanged.
// In the next line, do NOT set ptr to the result. If you do, it will leak!
ASSERT_EQ(nullptr, CPLRealloc(ptr, -1));
ASSERT_EQ(CPLE_AppDefined, CPLGetLastErrorNo());
// Pointer still valid.
ptr = CPLRealloc(ptr, 20);
ASSERT_NE(nullptr, ptr);
// Reallocate the same size leading to a copy, but no resize.
void *ptr2 = CPLRealloc(ptr, 20);
// The contents of ptr have been freed.
// TODO(schwehr): Fails with optimized compile, but succeeds with debug.
// ASSERT_NE(ptr, ptr2);
CPLFree(ptr2);
// Demonstrate leak potential.
ptr = CPLRealloc(nullptr, 10);
ptr2 = ptr; // Backup the pointer.
ptr = CPLRealloc(ptr, -1); // Leak by design.
ASSERT_EQ(nullptr, ptr);
CPLFree(ptr2);
// Not capturing the output of CPLRealloc will also cause a leak.
// CPLRealloc(ptr, 10);
// A size of 0 should free the pointer.
// It is safe to pass a nullptr.
ptr = CPLRealloc(nullptr, 0);
ASSERT_EQ(nullptr, ptr);
ptr = CPLRealloc(ptr, 10);
// Free a block of data.
ptr = CPLRealloc(ptr, 0);
ASSERT_EQ(nullptr, ptr);
}
// Tests using the portable version of strdup and inplace lowercase of a string.
TEST(CplConvTest, Strings) {
// Unlike strdup, CPLStrdup of a nullptr returns the empty string
// per documentation.
char *ptr = CPLStrdup(nullptr);
ASSERT_STREQ("", ptr);
CPLFree(ptr);
ptr = CPLStrdup("abc123");
ASSERT_STREQ("abc123", ptr);
CPLFree(ptr);
char str[] = "AbC123";
// Converts in place to lower case and returns the original pointer.
ASSERT_EQ(str, CPLStrlwr(str));
ASSERT_STREQ("abc123", str);
}
// Tests the fgets wrapper.
TEST(CplConvTest, CplFGets) {
ASSERT_EQ(nullptr, CPLFGets(nullptr, 0, nullptr));
ASSERT_EQ(nullptr, CPLFGets(nullptr, 1, reinterpret_cast<FILE*>(1)));
ASSERT_EQ(nullptr, CPLFGets(reinterpret_cast<char*>(1), 0,
reinterpret_cast<FILE*>(1)));
ASSERT_EQ(nullptr, CPLFGets(reinterpret_cast<char*>(1), 1, nullptr));
#if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L // fmemopen.
string src_buf("\nfoo\rbar\r\nmuch longer line\n \n\n");
FILE *src = fmemopen(const_cast<char*>(src_buf.c_str()), src_buf.size(), "r");
const size_t kBufSize = 10;
char buf[kBufSize];
ASSERT_STREQ("", CPLFGets(buf, kBufSize, src));
ASSERT_STREQ("foo", CPLFGets(buf, kBufSize, src));
ASSERT_STREQ("bar", CPLFGets(buf, kBufSize, src));
ASSERT_STREQ("much long", CPLFGets(buf, kBufSize, src));
ASSERT_STREQ("er line", CPLFGets(buf, kBufSize, src));
ASSERT_STREQ(" ", CPLFGets(buf, kBufSize, src));
ASSERT_STREQ("", CPLFGets(buf, kBufSize, src));
ASSERT_STREQ(nullptr, CPLFGets(buf, kBufSize, src));
#endif // POSIX-2008.1
}
// Tests replacement for CPLFGets that has an internal line buffer.
// CPLReadLine is not thread safe.
TEST(CplConvTest, CplReadLine) {
#if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L // fmemopen.
string src_buf("\na\rb\r\nc\n \n");
FILE *src = fmemopen(const_cast<char*>(src_buf.c_str()), src_buf.size(), "r");
ASSERT_STREQ("", CPLReadLine(src));
ASSERT_STREQ("a", CPLReadLine(src));
ASSERT_STREQ("b", CPLReadLine(src));
ASSERT_STREQ("c", CPLReadLine(src));
ASSERT_STREQ(" ", CPLReadLine(src));
ASSERT_STREQ(nullptr, CPLReadLine(src));
#endif // POSIX-2008.1
}
// TODO(schwehr): Write tests for CPLReadLineL.
// TODO(schwehr): Write tests for CPLReadLine2L.
// Tests returning a length limited copy of a string with optional
// removal of trailing white space and optional replacement of colons
// with under bars. The last 2 integer parameters are treated as booleans.
TEST(CplConvTest, CplScanString) {
ASSERT_EQ(nullptr, CPLScanString(nullptr, 0, 0, 0));
char *ptr = CPLScanString("", 0, 0, 0);
ASSERT_STREQ("", ptr);
CPLFree(ptr);
// nMaxLength greater than the string.
ptr = CPLScanString(" Foo", 10, 0, 0);
CHECK_STREQ(" Foo", ptr);
CPLFree(ptr);
// nMaxLength less than the string.
ptr = CPLScanString(" FooBar", 4, 0, 0);
ASSERT_STREQ(" Foo", ptr);
CPLFree(ptr);
// Trim trailing spaces.
ptr = CPLScanString(" Foo ", 5, 1, 0);
ASSERT_STREQ(" Foo", ptr);
CPLFree(ptr);
// "Normalization" means replace all occurrences of ":" with "_".
ptr = CPLScanString(" :_:_", 5, 0, 1);
CHECK_STREQ(" ____", ptr);
CPLFree(ptr);
// Trim trailing spaces and "normalize" by replacing ":" with "_".
ptr = CPLScanString(" :_ _: ", 6, 1, 1);
CHECK_STREQ(" __ __", ptr);
CPLFree(ptr);
}
// Tests converting a string to an integer. CplScanLong is a wrapper
// around atoi that adds setting a maximum number of characters.
TEST(CplConvTest, CplScanLong) {
// A maximum length of 0 might be undefined based on the definitions
// atoi and strtol.
ASSERT_EQ(0, CPLScanLong("", 0));
ASSERT_EQ(0, CPLScanLong("9", 0));
ASSERT_EQ(0, CPLScanLong("a", 1));
ASSERT_EQ(0, CPLScanLong("+", 1));
ASSERT_EQ(0, CPLScanLong("-", 1));
ASSERT_EQ(0, CPLScanLong("0", 1));
ASSERT_EQ(0, CPLScanLong("0", 2));
ASSERT_EQ(1, CPLScanLong("1", 1));
ASSERT_EQ(1, CPLScanLong("1", 2));
ASSERT_EQ(1, CPLScanLong("12", 1));
ASSERT_EQ(0, CPLScanLong("+1", 1));
ASSERT_EQ(1, CPLScanLong("+1", 2));
ASSERT_EQ(-1, CPLScanLong("-1", 2));
ASSERT_EQ(-1, CPLScanLong("-1", 3));
// Hex and octal are not supported.
ASSERT_EQ(0, CPLScanLong("0xF", 3));
ASSERT_EQ(10, CPLScanLong("010", 3));
ASSERT_EQ(2, CPLScanLong(" 2", 2));
ASSERT_EQ(3, CPLScanLong("\t\n3", 3));
ASSERT_EQ(4, CPLScanLong("4 ", 2));
ASSERT_EQ(5, CPLScanLong(" 5 ", 3));
ASSERT_EQ(6, CPLScanLong(" 6 1", 4));
ASSERT_EQ(7, CPLScanLong(" 7-", 3));
ASSERT_EQ(8, CPLScanLong(" 8a", 3));
ASSERT_EQ(9, CPLScanLong(" 9.1", 4));
if (sizeof(long) > 4) { // NOLINT
ASSERT_EQ(1234567890123, CPLScanLong("1234567890123", 9999));
ASSERT_EQ(-1234567890123, CPLScanLong("-1234567890123", 9999));
}
}
// Tests converting string to unsigned long. CplScanULong is a
// wrapper around strtoul that adds setting a maximum number of
// characters.
TEST(CplConvTest, CplScanULong) {
ASSERT_EQ(0, CPLScanULong("", 0));
ASSERT_EQ(0, CPLScanULong("9", 0));
ASSERT_EQ(0, CPLScanULong("a", 1));
ASSERT_EQ(0, CPLScanULong("+", 1));
ASSERT_EQ(0, CPLScanULong("-", 1));
ASSERT_EQ(0, CPLScanULong("0", 1));
ASSERT_EQ(1, CPLScanULong("1", 1));
ASSERT_EQ(1, CPLScanULong("1", 2));
ASSERT_EQ(1, CPLScanULong("12", 1));
ASSERT_EQ(1, CPLScanULong("+1", 2));
// strtoul does parse the "-". It negates the unsigned portion and then
// casts to unsigned long.
ASSERT_EQ(static_cast<unsigned long>(-1), CPLScanULong("-1", 2)); // NOLINT
ASSERT_EQ(static_cast<unsigned long>(-2), CPLScanULong("-2", 2)); // NOLINT
// Hex and octal are not supported.
ASSERT_EQ(0, CPLScanULong("0xF", 3));
ASSERT_EQ(10, CPLScanULong("010", 3));
ASSERT_EQ(2, CPLScanULong(" 2", 2));
ASSERT_EQ(3, CPLScanULong("\t\n3", 3));
ASSERT_EQ(4, CPLScanULong("4 ", 2));
ASSERT_EQ(5, CPLScanULong(" 5 ", 3));
ASSERT_EQ(6, CPLScanULong(" 6 1", 4));
ASSERT_EQ(7, CPLScanULong(" 7-", 3));
ASSERT_EQ(8, CPLScanULong(" 8a", 3));
ASSERT_EQ(9, CPLScanULong(" 9.1", 4));
if (sizeof(unsigned long) > 4) { // NOLINT
ASSERT_EQ(1234567890123, CPLScanULong("1234567890123", 9999));
ASSERT_EQ(static_cast<unsigned long>(-1234567890123), // NOLINT
CPLScanULong("-1234567890123", 9999));
}
}
// TODO(schwehr): Test the rest of cpl_conv.cpp.
} // namespace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment