mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Add 'src/libpsl-native/' from commit '4bb241ad11c54985617343fbeb8700cfdcdf7ae0'
git-subtree-dir: src/libpsl-native git-subtree-mainline:2264613ae3git-subtree-split:4bb241ad11
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
CTestTestfile.cmake
|
||||
Testing/
|
||||
test/psl-native-test
|
||||
src/libpsl-native.so
|
||||
src/libpsl-native.dylib
|
||||
test/native-tests.xml
|
||||
@@ -0,0 +1,4 @@
|
||||
[submodule "test/googletest"]
|
||||
path = test/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
ignore = dirty
|
||||
@@ -0,0 +1,10 @@
|
||||
cmake_minimum_required(VERSION 2.8.4)
|
||||
project(PSL-NATIVE)
|
||||
|
||||
add_compile_options(-std=c++11 -Wall -Werror)
|
||||
set(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/../Microsoft.PowerShell.Linux.Host")
|
||||
|
||||
# test in BUILD_DIR
|
||||
enable_testing()
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(test)
|
||||
@@ -0,0 +1,56 @@
|
||||
# libpsl-native
|
||||
|
||||
This library provides functionality missing from .NET Core via system calls,
|
||||
that are called from from the `CorePsPlatform.cs` file of PowerShell. The
|
||||
method to do this is a Platform Invoke, which is C#'s Foreign Function
|
||||
Interface to C code (and C++ by way of `extern C`).
|
||||
|
||||
## Build
|
||||
|
||||
[CMake][] is used to build the project, which results in a `libpsl-native.so`
|
||||
library on Linux, and `libpsl-native.dylib` on OS X.
|
||||
|
||||
```sh
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug .
|
||||
make -j
|
||||
```
|
||||
|
||||
[CMake]: https://cmake.org/cmake/help/v2.8.12/cmake.html
|
||||
|
||||
## Test
|
||||
|
||||
The [Google Test][] framework is used for unit tests.
|
||||
|
||||
Use either `make test` or `ctest --verbose` for more output.
|
||||
|
||||
[Google Test]: https://github.com/google/googletest/tree/release-1.7.0
|
||||
|
||||
## Notes
|
||||
|
||||
Marshalling data from native to managed code is much easier on Linux than it is
|
||||
on Windows. For instance, to return a string, you simply return a copy of it on
|
||||
the heap. Since only one memory allocator is used on Linux, the .NET runtime
|
||||
has no problem later freeing the buffer. Additionally, .NET presumes that the
|
||||
codepage "Ansi" on Linux is always UTF-8. So just marshal the string as
|
||||
`UnmanagedType.LPStr`.
|
||||
|
||||
### C# (Managed)
|
||||
|
||||
```c#
|
||||
[DllImport("libpsl-native", CharSet = CharSet.Ansi)]
|
||||
[return: MarshalAs(UnmanagedType.LPStr)]
|
||||
internal static extern string GetSomeString();
|
||||
```
|
||||
|
||||
### C (Native)
|
||||
|
||||
```c
|
||||
char *GetSomeString()
|
||||
{
|
||||
return strdup("some string");
|
||||
}
|
||||
```
|
||||
|
||||
The CoreFX team has an excellent guide for [UNIX Interop][].
|
||||
|
||||
[UNIX Interop]: https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/interop-guidelines.md#unix-shims
|
||||
@@ -0,0 +1,14 @@
|
||||
add_library(psl-native SHARED
|
||||
getcurrentprocessorid.cpp
|
||||
getusername.cpp
|
||||
getcomputername.cpp
|
||||
getlinkcount.cpp
|
||||
getfullyqualifiedname.cpp
|
||||
issymlink.cpp
|
||||
isexecutable.cpp
|
||||
setdate.cpp
|
||||
createhardlink.cpp
|
||||
createsymlink.cpp
|
||||
followsymlink.cpp)
|
||||
|
||||
target_include_directories(psl-native PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@@ -0,0 +1,115 @@
|
||||
//! @file createsymlink.cpp
|
||||
//! @author George FLeming <v-geflem@microsoft.com>
|
||||
//! @brief create new hard link
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include "createhardlink.h"
|
||||
|
||||
//! @brief Createhardlink create new symbolic link
|
||||
//!
|
||||
//! Createhardlink
|
||||
//!
|
||||
//! @param[in] link
|
||||
//! @parblock
|
||||
//! A pointer to the buffer that contains the symbolic link to create
|
||||
//!
|
||||
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
|
||||
//! @endparblock
|
||||
//!
|
||||
//! @param[in] target
|
||||
//! @parblock
|
||||
//! A pointer to the buffer that contains the existing file
|
||||
//!
|
||||
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
|
||||
//! @endparblock
|
||||
//!
|
||||
//! @exception errno Passes these errors via errno to GetLastError:
|
||||
//! - ERROR_INVALID_PARAMETER: parameter is not valid
|
||||
//! - ERROR_FILE_NOT_FOUND: file does not exist
|
||||
//! - ERROR_ACCESS_DENIED: access is denied
|
||||
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
|
||||
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
|
||||
//! - ERROR_TOO_MANY_LINK: max number of hard links has been exceeded
|
||||
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
|
||||
//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file
|
||||
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
|
||||
//! - ERROR_BUFFER_OVERFLOW: file name is too long
|
||||
//! - ERROR_INVALID_FUNCTION: incorrect function
|
||||
//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters
|
||||
//!
|
||||
//! @retval 1 if creation is successful
|
||||
//! @retval 0 if createion failed
|
||||
//!
|
||||
|
||||
int32_t CreateHardLink(const char *newlink, const char *target)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
// Check parameters
|
||||
if (!newlink || !target)
|
||||
{
|
||||
errno = ERROR_INVALID_PARAMETER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int returnCode = link(target, newlink);
|
||||
|
||||
if (returnCode == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch(errno)
|
||||
{
|
||||
case EACCES:
|
||||
errno = ERROR_ACCESS_DENIED;
|
||||
break;
|
||||
case EDQUOT:
|
||||
errno = ERROR_DISK_FULL;
|
||||
break;
|
||||
case EEXIST:
|
||||
errno = ERROR_FILE_EXISTS;
|
||||
break;
|
||||
case EFAULT:
|
||||
errno = ERROR_INVALID_ADDRESS;
|
||||
break;
|
||||
case EIO:
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
break;
|
||||
case ELOOP:
|
||||
errno = ERROR_TOO_MANY_LINKS;
|
||||
break;
|
||||
case EMLINK:
|
||||
errno = ERROR_TOO_MANY_LINKS;
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
errno = ERROR_BAD_PATH_NAME;
|
||||
break;
|
||||
case ENOENT:
|
||||
errno = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case ENOMEM:
|
||||
errno = ERROR_OUTOFMEMORY;
|
||||
break;
|
||||
case ENOTDIR:
|
||||
errno = ERROR_INVALID_NAME;
|
||||
break;
|
||||
case ENOSPC:
|
||||
errno = ERROR_DISK_FULL;
|
||||
break;
|
||||
case EPERM:
|
||||
errno = ERROR_ACCESS_DENIED;
|
||||
break;
|
||||
case EROFS:
|
||||
errno = ERROR_ACCESS_DENIED;
|
||||
break;
|
||||
case EXDEV:
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
break;
|
||||
default:
|
||||
errno = ERROR_INVALID_FUNCTION;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
int32_t CreateHardLink(const char *link, const char *target);
|
||||
|
||||
PAL_END_EXTERNC
|
||||
@@ -0,0 +1,106 @@
|
||||
//! @file createsymlink.cpp
|
||||
//! @author George FLeming <v-geflem@microsoft.com>
|
||||
//! @brief create new symbolic link
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include "createsymlink.h"
|
||||
|
||||
//! @brief Createsymlink create new symbolic link
|
||||
//!
|
||||
//! Createsymlink
|
||||
//!
|
||||
//! @param[in] link
|
||||
//! @parblock
|
||||
//! A pointer to the buffer that contains the symbolic link to create
|
||||
//!
|
||||
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
|
||||
//! @endparblock
|
||||
//!
|
||||
//! @param[in] target
|
||||
//! @parblock
|
||||
//! A pointer to the buffer that contains the existing file
|
||||
//!
|
||||
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
|
||||
//! @endparblock
|
||||
//!
|
||||
//! @exception errno Passes these errors via errno to GetLastError:
|
||||
//! - ERROR_INVALID_PARAMETER: parameter is not valid
|
||||
//! - ERROR_FILE_NOT_FOUND: file does not exist
|
||||
//! - ERROR_ACCESS_DENIED: access is denied
|
||||
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
|
||||
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
|
||||
//! - ERROR_STOPPED_ON_SYMLINK: the operation stopped after reaching a symbolic link
|
||||
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
|
||||
//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file
|
||||
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
|
||||
//! - ERROR_BUFFER_OVERFLOW: file name is too long
|
||||
//! - ERROR_INVALID_FUNCTION: incorrect function
|
||||
//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters
|
||||
//!
|
||||
//! @retval 1 if creation is successful
|
||||
//! @retval 0 if createion failed
|
||||
//!
|
||||
|
||||
int32_t CreateSymLink(const char *link, const char *target)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
// Check parameters
|
||||
if (!link || !target)
|
||||
{
|
||||
errno = ERROR_INVALID_PARAMETER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int returnCode = symlink(target, link);
|
||||
|
||||
if (returnCode == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch(errno)
|
||||
{
|
||||
case EACCES:
|
||||
errno = ERROR_ACCESS_DENIED;
|
||||
break;
|
||||
case EDQUOT:
|
||||
errno = ERROR_DISK_FULL;
|
||||
break;
|
||||
case EEXIST:
|
||||
errno = ERROR_FILE_EXISTS;
|
||||
break;
|
||||
case EFAULT:
|
||||
errno = ERROR_INVALID_ADDRESS;
|
||||
break;
|
||||
case EIO:
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
break;
|
||||
case ELOOP:
|
||||
errno = ERROR_STOPPED_ON_SYMLINK;
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
errno = ERROR_BAD_PATH_NAME;
|
||||
break;
|
||||
case ENOENT:
|
||||
errno = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case ENOMEM:
|
||||
errno = ERROR_OUTOFMEMORY;
|
||||
break;
|
||||
case ENOTDIR:
|
||||
errno = ERROR_INVALID_NAME;
|
||||
break;
|
||||
case ENOSPC:
|
||||
errno = ERROR_DISK_FULL;
|
||||
break;
|
||||
case EPERM:
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
break;
|
||||
default:
|
||||
errno = ERROR_INVALID_FUNCTION;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
int32_t CreateSymLink(const char *link, const char *target);
|
||||
|
||||
PAL_END_EXTERNC
|
||||
@@ -0,0 +1,90 @@
|
||||
//! @file followSymLink.cpp
|
||||
//! @author George FLeming <v-geflem@microsoft.com>
|
||||
//! @brief returns whether a path is a symbolic link
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "followsymlink.h"
|
||||
|
||||
//! @brief Followsymlink determines target path of a sym link
|
||||
//!
|
||||
//! Followsymlink
|
||||
//!
|
||||
//! @param[in] fileName
|
||||
//! @parblock
|
||||
//! A pointer to the buffer that contains the file name
|
||||
//!
|
||||
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
|
||||
//! @endparblock
|
||||
//!
|
||||
//! @exception errno Passes these errors via errno to GetLastError:
|
||||
//! - ERROR_INVALID_PARAMETER: parameter is not valid
|
||||
//! - ERROR_FILE_NOT_FOUND: file does not exist
|
||||
//! - ERROR_ACCESS_DENIED: access is denied
|
||||
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
|
||||
//! - ERROR_STOPPED_ON_SYMLINK: too many symbolic links
|
||||
//! - ERROR_GEN_FAILURE: I/O error occurred
|
||||
//! - ERROR_INVALID_NAME: file provided is not a symbolic link
|
||||
//! - ERROR_INVALID_FUNCTION: incorrect function
|
||||
//! - ERROR_BAD_PATH_NAME: pathname is too long
|
||||
//! - ERROR_OUTOFMEMORY insufficient kernal memory
|
||||
//!
|
||||
//! @retval target path, or NULL if unsuccessful
|
||||
//!
|
||||
|
||||
char* FollowSymLink(const char* fileName)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
// Check parameters
|
||||
if (!fileName)
|
||||
{
|
||||
errno = ERROR_INVALID_PARAMETER;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char buffer[PATH_MAX];
|
||||
ssize_t sz = readlink(fileName, buffer, PATH_MAX);
|
||||
|
||||
if (sz == -1)
|
||||
{
|
||||
switch(errno)
|
||||
{
|
||||
case EACCES:
|
||||
errno = ERROR_ACCESS_DENIED;
|
||||
break;
|
||||
case EFAULT:
|
||||
errno = ERROR_INVALID_ADDRESS;
|
||||
break;
|
||||
case EINVAL:
|
||||
errno = ERROR_INVALID_NAME;
|
||||
break;
|
||||
case EIO:
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
break;
|
||||
case ELOOP:
|
||||
errno = ERROR_STOPPED_ON_SYMLINK;
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
errno = ERROR_BAD_PATH_NAME;
|
||||
break;
|
||||
case ENOENT:
|
||||
errno = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case ENOMEM:
|
||||
errno = ERROR_OUTOFMEMORY;
|
||||
break;
|
||||
case ENOTDIR:
|
||||
errno = ERROR_BAD_PATH_NAME;
|
||||
break;
|
||||
default:
|
||||
errno = ERROR_INVALID_FUNCTION;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer[sz] = '\0';
|
||||
return strndup(buffer, sz + 1);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
char* FollowSymLink(const char* fileName);
|
||||
|
||||
PAL_END_EXTERNC
|
||||
@@ -0,0 +1,45 @@
|
||||
//! @file getcomputername.cpp
|
||||
//! @author George Fleming <v-geflem@microsoft>
|
||||
//! @brief Implements GetComputerName Win32 API
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include "getcomputername.h"
|
||||
|
||||
//! @brief GetComputerName retrieves the name of the host associated with
|
||||
//! the current thread.
|
||||
//!
|
||||
//! @exception errno Passes these errors via errno to GetLastError:
|
||||
//! - ERROR_INVALID_FUNCTION: getlogin_r() returned an unrecognized error code
|
||||
//! - ERROR_INVALID_ADDRESS: buffer is an invalid address
|
||||
//! - ERROR_GEN_FAILURE: buffer not large enough
|
||||
//!
|
||||
//! @retval username as UTF-8 string, or null if unsuccessful
|
||||
|
||||
char* GetComputerName()
|
||||
{
|
||||
errno = 0;
|
||||
// Get computername from system, note that gethostname(2) gets the
|
||||
// nodename from uname
|
||||
std::string computername(_POSIX_HOST_NAME_MAX, 0);
|
||||
int err = gethostname(&computername[0], computername.length());
|
||||
// Map errno to Win32 Error Codes
|
||||
if (err != 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EFAULT:
|
||||
errno = ERROR_INVALID_ADDRESS;
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
break;
|
||||
default:
|
||||
errno = ERROR_INVALID_FUNCTION;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return strdup(computername.c_str());
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
char *GetComputerName();
|
||||
|
||||
PAL_END_EXTERNC
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "getcurrentprocessorid.h"
|
||||
#include <unistd.h>
|
||||
|
||||
int32_t GetCurrentProcessId()
|
||||
{
|
||||
pid_t pid = getpid();
|
||||
return static_cast<int32_t>(pid);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
int32_t GetCurrentProcessId();
|
||||
|
||||
PAL_END_EXTERNC
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "getcurrentthreadid.h"
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
HANDLE GetCurrentThreadId()
|
||||
{
|
||||
pid_t tid = pthread_self();
|
||||
return reinterpret_cast<HANDLE>(tid);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
HANDLE GetCurrentThreadId();
|
||||
|
||||
PAL_END_EXTERNC
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
//! @file getfullyqualifiedname.cpp
|
||||
//! @author George Fleming <v-geflem@microsoft.com>
|
||||
//! @brief Implements GetFullyQualifiedName on Linux
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include "getcomputername.h"
|
||||
#include "getfullyqualifiedname.h"
|
||||
|
||||
//! @brief GetFullyQualifiedName retrieves the fully qualifed dns name of the host
|
||||
//!
|
||||
//! @exception errno Passes these errors via errno to GetLastError:
|
||||
//! - ERROR_INVALID_FUNCTION: getlogin_r() returned an unrecognized error code (from GetComputerName)
|
||||
//! - ERROR_INVALID_ADDRESS: buffer is an invalid address (from GetComputerName)
|
||||
//! - ERROR_GEN_FAILURE: buffer not large enough (from GetComputerName)
|
||||
//! - ERROR_BAD_NET_NAME: Cannot determine network short name
|
||||
//!
|
||||
//! @retval username as UTF-8 string, or null if unsuccessful
|
||||
//!
|
||||
|
||||
char *GetFullyQualifiedName()
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
char *computerName = GetComputerName();
|
||||
if (computerName == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strchr(computerName, '.') != NULL)
|
||||
{
|
||||
return computerName;
|
||||
}
|
||||
|
||||
struct addrinfo hints, *info;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC; /*either IPV4 or IPV6*/
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
|
||||
/* There are several ways to get the domain name:
|
||||
* uname(2), gethostbyname(3), resolver(3), getdomainname(2),
|
||||
* and getaddrinfo(3). Some of these are not portable, some aren't
|
||||
* POSIX compliant, and some are being deprecated. getaddrinfo seems
|
||||
* to be the best choice.
|
||||
*/
|
||||
if (getaddrinfo(computerName, "http", &hints, &info) != 0)
|
||||
{
|
||||
errno = ERROR_BAD_NET_NAME;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// info is actually a link-list. We'll just return the first full name
|
||||
|
||||
char *fullName = strndup(info->ai_canonname, strlen(info->ai_canonname));
|
||||
|
||||
freeaddrinfo(info);
|
||||
free(computerName);
|
||||
return fullName;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
char *GetFullyQualifiedName();
|
||||
|
||||
PAL_END_EXTERNC
|
||||
@@ -0,0 +1,104 @@
|
||||
//! @file getlinkcount.cpp
|
||||
//! @author George FLeming <v-geflem@microsoft.com>
|
||||
//! @brief Retrieve link count of a file
|
||||
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include "getlinkcount.h"
|
||||
|
||||
//! @brief GetLinkCount retrieves the file link count (number of hard links)
|
||||
//! for the given file
|
||||
//!
|
||||
//! GetLinkCount
|
||||
//!
|
||||
//! @param[in] fileName
|
||||
//! @parblock
|
||||
//! A pointer to the buffer that contains the file name
|
||||
//!
|
||||
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
|
||||
//! @endparblock
|
||||
//!
|
||||
//! @param[out] count
|
||||
//! @parblock
|
||||
//! This function returns the number of hard links associated with this file
|
||||
//! @endparblock
|
||||
//!
|
||||
//! @exception errno Passes these errors via errno to GetLastError:
|
||||
//! - ERROR_INVALID_PARAMETER: parameter is not valid
|
||||
//! - ERROR_FILE_NOT_FOUND: file does not exist
|
||||
//! - ERROR_ACCESS_DENIED: access is denied
|
||||
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
|
||||
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
|
||||
//! - ERROR_STOPPED_ON_SYMLINK: the operation stopped after reaching a symbolic link
|
||||
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
|
||||
//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file
|
||||
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
|
||||
//! - ERROR_BUFFER_OVERFLOW: file name is too long
|
||||
//! - ERROR_INVALID_FUNCTION: incorrect function
|
||||
//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters
|
||||
//!
|
||||
//! @retval 1 If the function succeeds, and the variable pointed to by buffer contains
|
||||
//! infomation about the files
|
||||
//! @retval 0 If the function fails, the return value is zero. To get
|
||||
//! extended error information, call GetLastError.
|
||||
//!
|
||||
|
||||
int32_t GetLinkCount(const char* fileName, int32_t *count)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
// Check parameters
|
||||
if (!fileName)
|
||||
{
|
||||
errno = ERROR_INVALID_PARAMETER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct stat statBuf;
|
||||
|
||||
int returnCode = lstat(fileName, &statBuf);
|
||||
|
||||
if (returnCode != 0)
|
||||
{
|
||||
switch(errno)
|
||||
{
|
||||
case EACCES:
|
||||
errno = ERROR_ACCESS_DENIED;
|
||||
break;
|
||||
case EBADF:
|
||||
errno = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case EFAULT:
|
||||
errno = ERROR_INVALID_ADDRESS;
|
||||
break;
|
||||
case ELOOP:
|
||||
errno = ERROR_STOPPED_ON_SYMLINK;
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
break;
|
||||
case ENOENT:
|
||||
errno = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case ENOMEM:
|
||||
errno = ERROR_NO_SUCH_USER;
|
||||
break;
|
||||
case ENOTDIR:
|
||||
errno = ERROR_INVALID_NAME;
|
||||
break;
|
||||
case EOVERFLOW:
|
||||
errno = ERROR_BUFFER_OVERFLOW;
|
||||
break;
|
||||
default:
|
||||
errno = ERROR_INVALID_FUNCTION;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
*count = statBuf.st_nlink;
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
int32_t GetLinkCount(const char* fileName, int32_t *count);
|
||||
|
||||
PAL_END_EXTERNC
|
||||
@@ -0,0 +1,65 @@
|
||||
//! @file getusername.cpp
|
||||
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
|
||||
//! @brief Implements GetUserName for Linux
|
||||
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <pwd.h>
|
||||
#include "getusername.h"
|
||||
|
||||
//! @brief GetUserName retrieves the name of the user associated with
|
||||
//! the current thread.
|
||||
//!
|
||||
//! @exception errno Passes these errors via errno to GetLastError:
|
||||
//! - ERROR_INVALID_PARAMETER: parameter is not valid
|
||||
//! - ERROR_NO_SUCH_USER: there was no corresponding user
|
||||
//! - ERROR_GEN_FAILURE: sysconf() or getpwuid() failed for unknown reasons
|
||||
//!
|
||||
//! @retval username as UTF-8 string, or null if unsuccessful
|
||||
char* GetUserName()
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
struct passwd pwd;
|
||||
struct passwd* result;
|
||||
// gets the initial suggested size for buf
|
||||
int buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
if (buflen == -1)
|
||||
{
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
return NULL;
|
||||
}
|
||||
std::string buf(buflen, 0);
|
||||
|
||||
// geteuid() gets the effective user ID of the calling process, and is always successful
|
||||
int ret = getpwuid_r(geteuid(), &pwd, &buf[0], buflen, &result);
|
||||
|
||||
// Map errno to Win32 Error Codes
|
||||
if (ret)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case ENOENT:
|
||||
case ESRCH:
|
||||
case EBADF:
|
||||
case EPERM:
|
||||
errno = ERROR_NO_SUCH_USER;
|
||||
break;
|
||||
default:
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check if no user matched
|
||||
if (result == NULL)
|
||||
{
|
||||
errno = ERROR_NO_SUCH_USER;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// allocate copy on heap so CLR can free it
|
||||
return strdup(result->pw_name);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
char* GetUserName();
|
||||
|
||||
PAL_END_EXTERNC
|
||||
@@ -0,0 +1,81 @@
|
||||
//! @file isExecutable.cpp
|
||||
//! @author George FLeming <v-geflem@microsoft.com>
|
||||
//! @brief returns whether a file is executable
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include "isexecutable.h"
|
||||
|
||||
//! @brief IsExecutable determines if path is executable
|
||||
//!
|
||||
//! IsExecutable
|
||||
//!
|
||||
//! @param[in] fileName
|
||||
//! @parblock
|
||||
//! A pointer to the buffer that contains the file name
|
||||
//!
|
||||
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
|
||||
//! @endparblock
|
||||
//!
|
||||
//! @exception errno Passes these errors via errno to GetLastError:
|
||||
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
|
||||
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
|
||||
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
|
||||
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
|
||||
//! - ERROR_INVALID_FUNCTION: incorrect function
|
||||
//! - ERROR_INVALID_PARAMETER: parameter to access(2) call is incorrect
|
||||
//!
|
||||
//! @retval 1 if path is an executable
|
||||
//! @retval 0 if path is not a executable
|
||||
//! @retval -1 If the function fails.. To get extended error information, call GetLastError.
|
||||
//!
|
||||
|
||||
int32_t IsExecutable(const char* fileName)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
// Check parameters
|
||||
if (!fileName)
|
||||
{
|
||||
errno = ERROR_INVALID_PARAMETER;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int returnCode = access(fileName, X_OK);
|
||||
|
||||
if (returnCode == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch(errno)
|
||||
{
|
||||
case EACCES:
|
||||
return 0;
|
||||
case EBADF:
|
||||
case ENOENT:
|
||||
errno = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case EFAULT:
|
||||
errno = ERROR_INVALID_ADDRESS;
|
||||
break;
|
||||
case ELOOP:
|
||||
errno = ERROR_STOPPED_ON_SYMLINK;
|
||||
break;
|
||||
case EIO:
|
||||
case ENOMEM:
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
break;
|
||||
case ENOTDIR:
|
||||
case ENAMETOOLONG:
|
||||
errno = ERROR_INVALID_NAME;
|
||||
break;
|
||||
case EINVAL:
|
||||
errno = ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
default:
|
||||
errno = ERROR_INVALID_FUNCTION;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
int32_t IsExecutable(const char* fileName);
|
||||
|
||||
PAL_END_EXTERNC
|
||||
@@ -0,0 +1,96 @@
|
||||
//! @file isSymLink.cpp
|
||||
//! @author George FLeming <v-geflem@microsoft.com>
|
||||
//! @brief returns whether a path is a symbolic link
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include "issymlink.h"
|
||||
|
||||
//! @brief IsSymLink determines if path is a symbolic link
|
||||
//!
|
||||
//! IsSymLink
|
||||
//!
|
||||
//! @param[in] fileName
|
||||
//! @parblock
|
||||
//! A pointer to the buffer that contains the file name
|
||||
//!
|
||||
//! char* is marshaled as an LPStr, which on Linux is UTF-8.
|
||||
//! @endparblock
|
||||
//!
|
||||
//! @exception errno Passes these errors via errno to GetLastError:
|
||||
//! - ERROR_INVALID_PARAMETER: parameter is not valid
|
||||
//! - ERROR_FILE_NOT_FOUND: file does not exist
|
||||
//! - ERROR_ACCESS_DENIED: access is denied
|
||||
//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified
|
||||
//! - ERROR_INVALID_ADDRESS: attempt to access invalid address
|
||||
//! - ERROR_STOPPED_ON_SYMLINK: the operation stopped after reaching a symbolic link
|
||||
//! - ERROR_GEN_FAILURE: device attached to the system is not functioning
|
||||
//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file
|
||||
//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect
|
||||
//! - ERROR_BUFFER_OVERFLOW: file name is too long
|
||||
//! - ERROR_INVALID_FUNCTION: incorrect function
|
||||
//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters
|
||||
//!
|
||||
//! @retval 1 if path is a symbolic link
|
||||
//! @retval 0 if path is not a symbolic link
|
||||
//! @retval -1 If the function fails.. To get extended error information, call GetLastError.
|
||||
//!
|
||||
|
||||
int32_t IsSymLink(const char* fileName)
|
||||
{
|
||||
|
||||
errno = 0;
|
||||
|
||||
// Check parameters
|
||||
if (!fileName)
|
||||
{
|
||||
errno = ERROR_INVALID_PARAMETER;
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct stat statBuf;
|
||||
|
||||
int returnCode = lstat(fileName, &statBuf);
|
||||
|
||||
if (returnCode != 0)
|
||||
{
|
||||
switch(errno)
|
||||
{
|
||||
case EACCES:
|
||||
errno = ERROR_ACCESS_DENIED;
|
||||
break;
|
||||
case EBADF:
|
||||
errno = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case EFAULT:
|
||||
errno = ERROR_INVALID_ADDRESS;
|
||||
break;
|
||||
case ELOOP:
|
||||
errno = ERROR_STOPPED_ON_SYMLINK;
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
errno = ERROR_GEN_FAILURE;
|
||||
break;
|
||||
case ENOENT:
|
||||
errno = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case ENOMEM:
|
||||
errno = ERROR_NO_SUCH_USER;
|
||||
break;
|
||||
case ENOTDIR:
|
||||
errno = ERROR_INVALID_NAME;
|
||||
break;
|
||||
case EOVERFLOW:
|
||||
errno = ERROR_BUFFER_OVERFLOW;
|
||||
break;
|
||||
default:
|
||||
errno = ERROR_INVALID_FUNCTION;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return S_ISLNK(statBuf.st_mode) ? 1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
int32_t IsSymLink(const char* fileName);
|
||||
|
||||
PAL_END_EXTERNC
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define ERROR_INVALID_PARAMETER 87
|
||||
#define ERROR_OUTOFMEMORY 14
|
||||
#define ERROR_BAD_ENVIRONMENT 0x0000000A
|
||||
#define ERROR_TOO_MANY_OPEN_FILES 0x00000004
|
||||
#define ERROR_INSUFFICIENT_BUFFER 0x0000007A
|
||||
#define ERROR_NO_ASSOCIATION 0x00000483
|
||||
#define ERROR_NO_SUCH_USER 0x00000525
|
||||
#define ERROR_INVALID_FUNCTION 0x00000001
|
||||
#define ERROR_INVALID_ADDRESS 0x000001e7
|
||||
#define ERROR_GEN_FAILURE 0x0000001F
|
||||
#define ERROR_ACCESS_DENIED 0x00000005
|
||||
#define ERROR_INVALID_NAME 0x0000007B
|
||||
#define ERROR_STOPPED_ON_SYMLINK 0x000002A9
|
||||
#define ERROR_BUFFER_OVERFLOW 0x0000006F
|
||||
#define ERROR_FILE_NOT_FOUND 0x00000002
|
||||
#define ERROR_BAD_PATH_NAME 0x000000A1
|
||||
#define ERROR_BAD_NET_NAME 0x00000043
|
||||
#define ERROR_DISK_FULL 0x00000070
|
||||
#define ERROR_FILE_EXISTS 0x00000050
|
||||
#define ERROR_TOO_MANY_LINKS 0x00000476
|
||||
|
||||
/*
|
||||
**==============================================================================
|
||||
**
|
||||
** PAL_BEGIN_EXTERNC
|
||||
** PAL_END_EXTERNC
|
||||
**
|
||||
**==============================================================================
|
||||
*/
|
||||
|
||||
#if defined(__cplusplus)
|
||||
# define PAL_BEGIN_EXTERNC extern "C" {
|
||||
# define PAL_END_EXTERNC }
|
||||
#else
|
||||
# define PAL_BEGIN_EXTERNC
|
||||
# define PAL_END_EXTERNC
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
//! @file setdate.cpp
|
||||
//! @author George FLeming <v-geflem@microsoft.com>
|
||||
//! @brief set local/system date and time
|
||||
|
||||
#include <errno.h>
|
||||
#include <langinfo.h>
|
||||
#include <locale.h>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include "setdate.h"
|
||||
|
||||
//! @brief SetDate sets the date and time on local computer. You must
|
||||
//! be super-user to set the time.
|
||||
//!
|
||||
//! SetDate
|
||||
//!
|
||||
//! @param[in] info
|
||||
//! @parblock
|
||||
//! A struct that contains program to execute and its parameters
|
||||
//!
|
||||
//! @exception errno Passes these errors via errno to GetLastError:
|
||||
//! - ERROR_BAD_ENVIRONMENT: locale is not UTF-8
|
||||
//! - ERROR_INVALID_PARAMETER: time was not passed in correctly
|
||||
//! - ERROR_ACCESS_DENIED: you must be super-user to set the date
|
||||
//!
|
||||
//! @retval 0 successfully set date
|
||||
//! @retval -1 if failure occurred. To get extended error information, call GetLastError.
|
||||
//!
|
||||
|
||||
int32_t SetDate(const SetDateInfo &info)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
// Select locale from environment
|
||||
setlocale(LC_ALL, "");
|
||||
// Check that locale is UTF-8
|
||||
if (nl_langinfo(CODESET) != std::string("UTF-8"))
|
||||
{
|
||||
errno = ERROR_BAD_ENVIRONMENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct tm bdTime;
|
||||
struct timeval tv;
|
||||
|
||||
bdTime.tm_year = info.Year - 1900;
|
||||
bdTime.tm_mon = info.Month - 1; // This is zero-based
|
||||
bdTime.tm_mday = info.Day;
|
||||
bdTime.tm_hour = info.Hour;
|
||||
bdTime.tm_min = info.Minute;
|
||||
bdTime.tm_sec = info.Second;
|
||||
bdTime.tm_isdst = info.DST;
|
||||
|
||||
time_t newTime = mktime(&bdTime);
|
||||
if (newTime == -1)
|
||||
{
|
||||
errno = ERROR_INVALID_PARAMETER;
|
||||
return -1;
|
||||
}
|
||||
|
||||
tv.tv_sec = newTime;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
int result = settimeofday(&tv, NULL);
|
||||
if (result == -1)
|
||||
{
|
||||
errno = ERROR_ACCESS_DENIED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "pal.h"
|
||||
|
||||
PAL_BEGIN_EXTERNC
|
||||
|
||||
typedef struct setDateInfo
|
||||
{
|
||||
// the order of members does matter here
|
||||
int32_t Year;
|
||||
int32_t Month;
|
||||
int32_t Day;
|
||||
int32_t Hour;
|
||||
int32_t Minute;
|
||||
int32_t Second;
|
||||
int32_t Millisecond;
|
||||
int32_t DST;
|
||||
} SetDateInfo;
|
||||
|
||||
int32_t SetDate(const SetDateInfo &info);
|
||||
|
||||
PAL_END_EXTERNC
|
||||
@@ -0,0 +1,22 @@
|
||||
add_subdirectory(googletest)
|
||||
|
||||
add_executable(psl-native-test
|
||||
test-locale.cpp
|
||||
test-getcurrentprocessid.cpp
|
||||
test-getusername.cpp
|
||||
test-getcomputername.cpp
|
||||
test-getlinkcount.cpp
|
||||
test-getfullyqualifiedname.cpp
|
||||
test-issymlink.cpp
|
||||
test-isexecutable.cpp
|
||||
test-createsymlink.cpp
|
||||
test-createhardlink.cpp
|
||||
main.cpp)
|
||||
|
||||
# manually include gtest headers
|
||||
target_include_directories(psl-native-test PRIVATE ${gtest_SOURCE_DIR}/include)
|
||||
|
||||
target_link_libraries(psl-native-test psl-native gtest)
|
||||
|
||||
add_test(NAME psl-native-test
|
||||
COMMAND psl-native-test --gtest_output=xml:native-tests.xml)
|
||||
Submodule
+1
Submodule src/libpsl-native/test/googletest added at c99458533a
@@ -0,0 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//! @file test-createhardlink.cpp
|
||||
//! @author George Fleming <v-geflem@microsoft.com>
|
||||
//! @brief Implements test for CreateHardLink()
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "getlinkcount.h"
|
||||
#include "createhardlink.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class CreateHardLinkTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
|
||||
static const int bufSize = 64;
|
||||
const string fileTemplate = "/tmp/symlinktest.fXXXXXX";
|
||||
const string dirTemplate = "/tmp/symlinktest.dXXXXXX";
|
||||
const string fileHardLink = "/tmp/symlinktest.flink";
|
||||
const string dirHardLink = "/tmp/symlinktest.dlink";
|
||||
char *file, *dir;
|
||||
char fileTemplateBuf[bufSize], dirTemplateBuf[bufSize];
|
||||
|
||||
CreateHardLinkTest()
|
||||
{
|
||||
// since mkstemp and mkdtemp modifies the template string, let's give them writable buffers
|
||||
strcpy(fileTemplateBuf, fileTemplate.c_str());
|
||||
strcpy(dirTemplateBuf, dirTemplate.c_str());
|
||||
|
||||
// First create a temp file
|
||||
int fd = mkstemp(fileTemplateBuf);
|
||||
EXPECT_TRUE(fd != -1);
|
||||
file = fileTemplateBuf;
|
||||
|
||||
// Create a temp directory
|
||||
dir = mkdtemp(dirTemplateBuf);
|
||||
EXPECT_TRUE(dir != NULL);
|
||||
|
||||
// Create hard link to file
|
||||
int ret1 = CreateHardLink(fileHardLink.c_str(), file);
|
||||
EXPECT_EQ(ret1, 1);
|
||||
|
||||
// Create hard link to directory - should fail
|
||||
int ret2 = CreateHardLink(dirHardLink.c_str(), dir);
|
||||
EXPECT_EQ(ret2, 0);
|
||||
}
|
||||
|
||||
~CreateHardLinkTest()
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = unlink(fileHardLink.c_str());
|
||||
EXPECT_EQ(0, ret);
|
||||
|
||||
ret = unlink(file);
|
||||
EXPECT_EQ(0, ret);
|
||||
|
||||
ret = rmdir(dir);
|
||||
EXPECT_EQ(0, ret);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(CreateHardLinkTest, FilePathNameIsNull)
|
||||
{
|
||||
int retVal = CreateHardLink(NULL, NULL);
|
||||
EXPECT_EQ(retVal, 0);
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
|
||||
}
|
||||
|
||||
TEST_F(CreateHardLinkTest, FilePathNameDoesNotExist)
|
||||
{
|
||||
std::string invalidFile = "/tmp/symlinktest_invalidFile";
|
||||
std::string invalidLink = "/tmp/symlinktest_invalidLink";
|
||||
|
||||
// make sure neither exists
|
||||
unlink(invalidFile.c_str());
|
||||
unlink(invalidLink.c_str());
|
||||
|
||||
int retVal = CreateHardLink(invalidLink.c_str(), invalidFile.c_str());
|
||||
EXPECT_EQ(retVal, 0);
|
||||
}
|
||||
|
||||
TEST_F(CreateHardLinkTest, VerifyLinkCount)
|
||||
{
|
||||
int count = 0;
|
||||
int retVal = GetLinkCount(fileHardLink.c_str(), &count);
|
||||
EXPECT_EQ(1, retVal);
|
||||
EXPECT_EQ(2, count);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
//! @file test-createsymlink.cpp
|
||||
//! @author George Fleming <v-geflem@microsoft.com>
|
||||
//! @brief Implements test for CreateSymLink() and FollowSymLink()
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "issymlink.h"
|
||||
#include "createsymlink.h"
|
||||
#include "followsymlink.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class CreateSymLinkTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
|
||||
static const int bufSize = 64;
|
||||
const string fileTemplate = "/tmp/symlinktest.fXXXXXX";
|
||||
const string dirTemplate = "/tmp/symlinktest.dXXXXXX";
|
||||
const string fileSymLink = "/tmp/symlinktest.flink";
|
||||
const string dirSymLink = "/tmp/symlinktest.dlink";
|
||||
char *file, *dir;
|
||||
char fileTemplateBuf[bufSize], dirTemplateBuf[bufSize];
|
||||
|
||||
CreateSymLinkTest()
|
||||
{
|
||||
// since mkstemp and mkdtemp modifies the template string, let's give them writable buffers
|
||||
strcpy(fileTemplateBuf, fileTemplate.c_str());
|
||||
strcpy(dirTemplateBuf, dirTemplate.c_str());
|
||||
|
||||
// First create a temp file
|
||||
int fd = mkstemp(fileTemplateBuf);
|
||||
EXPECT_TRUE(fd != -1);
|
||||
file = fileTemplateBuf;
|
||||
|
||||
// Create a temp directory
|
||||
dir = mkdtemp(dirTemplateBuf);
|
||||
EXPECT_TRUE(dir != NULL);
|
||||
|
||||
// Create symbolic link to file
|
||||
int ret1 = CreateSymLink(fileSymLink.c_str(), file);
|
||||
EXPECT_EQ(ret1, 1);
|
||||
|
||||
// Create symbolic link to directory
|
||||
int ret2 = CreateSymLink(dirSymLink.c_str(), dir);
|
||||
EXPECT_EQ(ret2, 1);
|
||||
}
|
||||
|
||||
~CreateSymLinkTest()
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = unlink(fileSymLink.c_str());
|
||||
EXPECT_EQ(0, ret);
|
||||
|
||||
ret = unlink(dirSymLink.c_str());
|
||||
EXPECT_EQ(0, ret);
|
||||
|
||||
ret = unlink(file);
|
||||
EXPECT_EQ(0, ret);
|
||||
|
||||
ret = rmdir(dir);
|
||||
EXPECT_EQ(0, ret);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(CreateSymLinkTest, FilePathNameIsNull)
|
||||
{
|
||||
int retVal = CreateSymLink(NULL, NULL);
|
||||
EXPECT_EQ(retVal, 0);
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
|
||||
}
|
||||
|
||||
TEST_F(CreateSymLinkTest, FilePathNameDoesNotExist)
|
||||
{
|
||||
std::string invalidFile = "/tmp/symlinktest_invalidFile";
|
||||
std::string invalidLink = "/tmp/symlinktest_invalidLink";
|
||||
|
||||
// make sure neither exists
|
||||
unlink(invalidFile.c_str());
|
||||
unlink(invalidLink.c_str());
|
||||
|
||||
// Linux allows creation of symbolic link that points to an invalid file
|
||||
int retVal = CreateSymLink(invalidLink.c_str(), invalidFile.c_str());
|
||||
EXPECT_EQ(retVal, 1);
|
||||
|
||||
std::string target = FollowSymLink(invalidLink.c_str());
|
||||
EXPECT_EQ(target, invalidFile);
|
||||
|
||||
unlink(invalidLink.c_str());
|
||||
}
|
||||
|
||||
TEST_F(CreateSymLinkTest, SymLinkToFile)
|
||||
{
|
||||
int retVal = IsSymLink(fileSymLink.c_str());
|
||||
EXPECT_EQ(1, retVal);
|
||||
|
||||
std::string target = FollowSymLink(fileSymLink.c_str());
|
||||
EXPECT_EQ(target, file);
|
||||
}
|
||||
|
||||
TEST_F(CreateSymLinkTest, SymLinkToDirectory)
|
||||
{
|
||||
int retVal = IsSymLink(dirSymLink.c_str());
|
||||
EXPECT_EQ(1, retVal);
|
||||
|
||||
std::string target = FollowSymLink(dirSymLink.c_str());
|
||||
EXPECT_EQ(target, dir);
|
||||
}
|
||||
|
||||
TEST_F(CreateSymLinkTest, SymLinkAgain)
|
||||
{
|
||||
int retVal = CreateSymLink(fileSymLink.c_str(), file);
|
||||
EXPECT_EQ(0, retVal);
|
||||
EXPECT_EQ(ERROR_FILE_EXISTS, errno);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//! @file test-getcomputername.cpp
|
||||
//! @author George Fleming <v-geflem@microsoft.com>
|
||||
//! @brief Unit tests for GetComputerName
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "getcomputername.h"
|
||||
|
||||
//! Test fixture for GetComputerNameTest
|
||||
class GetComputerNameTest : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TEST_F(GetComputerNameTest, Success)
|
||||
{
|
||||
char expectedComputerName[_POSIX_HOST_NAME_MAX];
|
||||
|
||||
// the gethostname system call gets the nodename from uname
|
||||
FILE *fPtr = popen("uname -n", "r");
|
||||
ASSERT_TRUE(fPtr != NULL);
|
||||
|
||||
char *linePtr = fgets(expectedComputerName, sizeof(expectedComputerName), fPtr);
|
||||
ASSERT_TRUE(linePtr != NULL);
|
||||
|
||||
// There's a tendency to have \n at end of fgets string, so remove it before compare
|
||||
size_t sz = strlen(expectedComputerName);
|
||||
if (sz > 0 && expectedComputerName[sz - 1] == '\n')
|
||||
{
|
||||
expectedComputerName[sz - 1] = '\0';
|
||||
}
|
||||
pclose(fPtr);
|
||||
|
||||
ASSERT_STREQ(GetComputerName(), expectedComputerName);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "getcurrentprocessorid.h"
|
||||
|
||||
// This is a very simple test case to show how tests can be written
|
||||
TEST(GetCurrentProcessId,simple)
|
||||
{
|
||||
const int32_t currentProcessId = GetCurrentProcessId();
|
||||
const pid_t pid = getpid();
|
||||
|
||||
// first make sure that on this platform those types are of the same size
|
||||
ASSERT_TRUE(sizeof(int32_t) >= sizeof(pid_t));
|
||||
|
||||
// now compare the actual values
|
||||
ASSERT_EQ(currentProcessId,static_cast<int32_t>(pid));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "getcurrentthreadid.h"
|
||||
#include <pthread.h>
|
||||
|
||||
TEST(GetCurrentThreadId,simple)
|
||||
{
|
||||
const HANDLE currentThreadId = GetCurrentThreadId();
|
||||
const pid_t tid = pthread_self();
|
||||
|
||||
// first make sure that on this platform those types are of the same size
|
||||
ASSERT_TRUE(sizeof(HANDLE) >= sizeof(pid_t));
|
||||
|
||||
// now compare the actual values
|
||||
ASSERT_EQ(currentThreadId,reinterpret_cast<HANDLE>(tid));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
//! @file test-getfullyqualifiedname.cpp
|
||||
//! @author George Fleming <v-geflem@microsoft.com>
|
||||
//! @brief Unit tests for GetFullyQualifiedName
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "getfullyqualifiedname.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <string>
|
||||
|
||||
//! Test fixture for GetComputerNameTest
|
||||
class GetFullyQualifiedNameTest : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TEST_F(GetFullyQualifiedNameTest, ValidateLinuxGetFullyQualifiedDomainName)
|
||||
{
|
||||
std::string actual(GetFullyQualifiedName());
|
||||
|
||||
std::string hostname(_POSIX_HOST_NAME_MAX, 0);
|
||||
ASSERT_FALSE(gethostname(&hostname[0], hostname.length()));
|
||||
// trim null characters from string
|
||||
hostname = std::string(hostname.c_str());
|
||||
|
||||
struct addrinfo hints, *info;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
ASSERT_FALSE(getaddrinfo(hostname.c_str(), "http", &hints, &info));
|
||||
|
||||
// Compare hostname part of FQDN
|
||||
ASSERT_EQ(hostname, actual.substr(0, hostname.length()));
|
||||
|
||||
// Compare canonical name to FQDN
|
||||
ASSERT_EQ(info->ai_canonname, actual);
|
||||
|
||||
freeaddrinfo(info);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
//! @file test-getlinkcount.cpp
|
||||
//! @author George Fleming <v-geflem@microsoft.com>
|
||||
//! @brief Implements test for getLinkCount()
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <pwd.h>
|
||||
#include <fstream>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "getlinkcount.h"
|
||||
|
||||
class getLinkCountTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
|
||||
static const int bufSize = 64;
|
||||
const std::string fileTemplate = "/tmp/createFile.XXXXXX";
|
||||
char fileTemplateBuf[bufSize];
|
||||
|
||||
int32_t count;
|
||||
char *file;
|
||||
|
||||
getLinkCountTest()
|
||||
{
|
||||
// since mkstemp modifies the template string, let's give it writable buffer
|
||||
strcpy(fileTemplateBuf, fileTemplate.c_str());
|
||||
|
||||
int fd = mkstemp(fileTemplateBuf);
|
||||
EXPECT_TRUE(fd != -1);
|
||||
file = fileTemplateBuf;
|
||||
}
|
||||
|
||||
void createFileForTesting(const std::string &theFile)
|
||||
{
|
||||
std::ofstream ofs;
|
||||
ofs.open(theFile, std::ofstream::out);
|
||||
ofs << "hi there, ms ostc!";
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
std::string createHardLink(const std::string &origFile)
|
||||
{
|
||||
std::string newFile = origFile + "_link";
|
||||
int ret = link(origFile.c_str(), newFile.c_str());
|
||||
EXPECT_EQ(0, ret);
|
||||
|
||||
return newFile;
|
||||
}
|
||||
|
||||
void removeFile(const std::string &fileName)
|
||||
{
|
||||
int ret = unlink(fileName.c_str());
|
||||
EXPECT_EQ(0, ret);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(getLinkCountTest, FilePathNameIsNull)
|
||||
{
|
||||
int32_t retVal = GetLinkCount(NULL, &count );
|
||||
ASSERT_FALSE(retVal);
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
|
||||
}
|
||||
|
||||
TEST_F(getLinkCountTest, FilePathNameDoesNotExist)
|
||||
{
|
||||
std::string invalidFile = "/tmp/createFile";
|
||||
int32_t retVal = GetLinkCount(invalidFile.c_str(), &count);
|
||||
ASSERT_FALSE(retVal);
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno);
|
||||
}
|
||||
|
||||
TEST_F(getLinkCountTest, LinkCountOfSinglyLinkedFile)
|
||||
{
|
||||
createFileForTesting(file);
|
||||
int32_t retVal = GetLinkCount(file, &count);
|
||||
ASSERT_TRUE(retVal);
|
||||
EXPECT_EQ(1, count);
|
||||
|
||||
removeFile(file);
|
||||
}
|
||||
|
||||
TEST_F(getLinkCountTest, LinkCountOfMultipliLinkedFile)
|
||||
{
|
||||
createFileForTesting(file);
|
||||
std::string newFile = createHardLink(file);
|
||||
int32_t retVal = GetLinkCount(file, &count);
|
||||
ASSERT_TRUE(retVal);
|
||||
EXPECT_EQ(2, count);
|
||||
|
||||
removeFile(file);
|
||||
removeFile(newFile);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//! @file test-getusername.cpp
|
||||
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
|
||||
//! @brief Unit tests for GetUserName
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <pwd.h>
|
||||
#include "getusername.h"
|
||||
|
||||
TEST(GetUserName, Success)
|
||||
{
|
||||
char* expected = getpwuid(geteuid())->pw_name;
|
||||
ASSERT_TRUE(expected != NULL);
|
||||
ASSERT_EQ(GetUserName(), std::string(expected));
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
//! @file test-isexecutable.cpp
|
||||
//! @author George Fleming <v-geflem@microsoft.com>
|
||||
//! @brief Implements test for isexecutable()
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "isexecutable.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class IsExecutableTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
|
||||
static const int bufSize = 64;
|
||||
const string fileTemplate = "/tmp/isexecutabletest.fXXXXXXX";
|
||||
const mode_t mode_700 = S_IRUSR | S_IWUSR | S_IXUSR;
|
||||
const mode_t mode_070 = S_IRGRP | S_IWGRP | S_IXGRP;
|
||||
const mode_t mode_007 = S_IROTH | S_IWOTH | S_IXOTH;
|
||||
const mode_t mode_777 = mode_700 | mode_070 | mode_007;
|
||||
const mode_t mode_444 = S_IRUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
char *file;
|
||||
char fileTemplateBuf[bufSize];
|
||||
|
||||
IsExecutableTest()
|
||||
{
|
||||
// since mkstemp modifies the template string, let's give it writable buffers
|
||||
strcpy(fileTemplateBuf, fileTemplate.c_str());
|
||||
|
||||
// First create a file
|
||||
int fd = mkstemp(fileTemplateBuf);
|
||||
EXPECT_TRUE(fd != -1);
|
||||
file = fileTemplateBuf;
|
||||
}
|
||||
|
||||
~IsExecutableTest()
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = unlink(file);
|
||||
EXPECT_EQ(0, ret);
|
||||
}
|
||||
|
||||
void ChangeFilePermission(const char* file, mode_t mode)
|
||||
{
|
||||
int ret = chmod(file, mode);
|
||||
EXPECT_EQ(ret, 0);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(IsExecutableTest, FilePathNameIsNull)
|
||||
{
|
||||
int32_t retVal = IsExecutable(NULL);
|
||||
EXPECT_EQ(retVal, -1);
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
|
||||
}
|
||||
|
||||
TEST_F(IsExecutableTest, FilePathNameDoesNotExist)
|
||||
{
|
||||
std::string invalidFile = "/tmp/isexecutabletest_invalidFile";
|
||||
int32_t retVal = IsExecutable(invalidFile.c_str());
|
||||
EXPECT_EQ(retVal, -1);
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno);
|
||||
}
|
||||
|
||||
TEST_F(IsExecutableTest, NormalFileIsNotIsexecutable)
|
||||
{
|
||||
int32_t retVal = IsExecutable(file);
|
||||
EXPECT_EQ(0, retVal);
|
||||
|
||||
ChangeFilePermission(file, mode_444);
|
||||
|
||||
retVal = IsExecutable(file);
|
||||
EXPECT_EQ(0, retVal);
|
||||
}
|
||||
|
||||
TEST_F(IsExecutableTest, FilePermission_700)
|
||||
{
|
||||
ChangeFilePermission(file, mode_700);
|
||||
|
||||
int32_t retVal = IsExecutable(file);
|
||||
EXPECT_EQ(1, retVal);
|
||||
}
|
||||
|
||||
TEST_F(IsExecutableTest, FilePermission_777)
|
||||
{
|
||||
ChangeFilePermission(file, mode_777);
|
||||
|
||||
int32_t retVal = IsExecutable(file);
|
||||
EXPECT_EQ(1, retVal);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
//! @file test-issymlink.cpp
|
||||
//! @author George Fleming <v-geflem@microsoft.com>
|
||||
//! @brief Implements test for isSymLink()
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "issymlink.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class isSymLinkTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
|
||||
static const int bufSize = 64;
|
||||
const string fileTemplate = "/tmp/symlinktest.fXXXXXX";
|
||||
const string dirTemplate = "/tmp/symlinktest.dXXXXXX";
|
||||
const string fileSymLink = "/tmp/symlinktest.flink";
|
||||
const string dirSymLink = "/tmp/symlinktest.dlink";
|
||||
char *file, *dir;
|
||||
char fileTemplateBuf[bufSize], dirTemplateBuf[bufSize];
|
||||
|
||||
isSymLinkTest()
|
||||
{
|
||||
// since mkstemp and mkdtemp modifies the template string, let's give them writable buffers
|
||||
strcpy(fileTemplateBuf, fileTemplate.c_str());
|
||||
strcpy(dirTemplateBuf, dirTemplate.c_str());
|
||||
|
||||
// First create a file
|
||||
int fd = mkstemp(fileTemplateBuf);
|
||||
EXPECT_TRUE(fd != -1);
|
||||
file = fileTemplateBuf;
|
||||
|
||||
// Create a temp directory
|
||||
dir = mkdtemp(dirTemplateBuf);
|
||||
EXPECT_TRUE(dir != NULL);
|
||||
|
||||
// Create symbolic link to file
|
||||
int ret1 = symlink(file, fileSymLink.c_str());
|
||||
EXPECT_EQ(ret1, 0);
|
||||
|
||||
// Create symbolic link to directory
|
||||
int ret2 = symlink(dir, dirSymLink.c_str());
|
||||
EXPECT_EQ(ret2, 0);
|
||||
}
|
||||
|
||||
~isSymLinkTest()
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = unlink(fileSymLink.c_str());
|
||||
EXPECT_EQ(0, ret);
|
||||
|
||||
ret = unlink(dirSymLink.c_str());
|
||||
EXPECT_EQ(0, ret);
|
||||
|
||||
ret = unlink(file);
|
||||
EXPECT_EQ(0, ret);
|
||||
|
||||
ret = rmdir(dir);
|
||||
EXPECT_EQ(0, ret);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(isSymLinkTest, FilePathNameIsNull)
|
||||
{
|
||||
int retVal = IsSymLink(NULL);
|
||||
EXPECT_EQ(retVal, -1);
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, errno);
|
||||
}
|
||||
|
||||
TEST_F(isSymLinkTest, FilePathNameDoesNotExist)
|
||||
{
|
||||
std::string invalidFile = "/tmp/symlinktest_invalidFile";
|
||||
int retVal = IsSymLink(invalidFile.c_str());
|
||||
EXPECT_EQ(retVal, -1);
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno);
|
||||
}
|
||||
|
||||
TEST_F(isSymLinkTest, NormalFileIsNotSymLink)
|
||||
{
|
||||
int retVal = IsSymLink(file);
|
||||
EXPECT_EQ(0, retVal);
|
||||
}
|
||||
|
||||
TEST_F(isSymLinkTest, SymLinkToFile)
|
||||
{
|
||||
int retVal = IsSymLink(fileSymLink.c_str());
|
||||
EXPECT_EQ(1, retVal);
|
||||
}
|
||||
|
||||
TEST_F(isSymLinkTest, NormalDirectoryIsNotSymbLink)
|
||||
{
|
||||
int retVal = IsSymLink(dir);
|
||||
EXPECT_EQ(0, retVal);
|
||||
}
|
||||
|
||||
TEST_F(isSymLinkTest, SymLinkToDirectory)
|
||||
{
|
||||
int retVal = IsSymLink(dirSymLink.c_str());
|
||||
EXPECT_EQ(1, retVal);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//! @file test-locale.cpp
|
||||
//! @author Alex Jordan <v-alexjo@microsoft.com>
|
||||
//! @brief Unit tests for linux locale
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <langinfo.h>
|
||||
#include <locale.h>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
//! Test fixture for LocaleTest
|
||||
|
||||
class LocaleTest : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TEST_F(LocaleTest, Success)
|
||||
{
|
||||
setlocale (LC_ALL, "");
|
||||
ASSERT_FALSE (nl_langinfo(CODESET) == NULL);
|
||||
ASSERT_TRUE(nl_langinfo(CODESET) == std::string("UTF-8"));
|
||||
}
|
||||
Reference in New Issue
Block a user