[libcamera-devel] [PATCH v4 1/6] libcamera: utils: Add a C++ dirname implementation

Kieran Bingham kieran.bingham at ideasonboard.com
Mon Feb 24 10:36:11 CET 2020


Hi Laurent,

On 22/02/2020 10:27, Laurent Pinchart wrote:
> Hi Kieran,
> 
> Thank you for the patch.
> 
> On Fri, Feb 21, 2020 at 04:31:25PM +0000, Kieran Bingham wrote:
>> Provide a std::string based implementation which conforms to the
>> behaviour of the dirname() fucntion defined by POSIX.
>>
>> Tests are added to cover expected corner cases of the implementation.
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
>> ---
>>  src/libcamera/include/utils.h |  1 +
>>  src/libcamera/utils.cpp       | 48 +++++++++++++++++++++++++++++++
>>  test/utils.cpp                | 54 +++++++++++++++++++++++++++++++++++
>>  3 files changed, 103 insertions(+)
>>
>> diff --git a/src/libcamera/include/utils.h b/src/libcamera/include/utils.h
>> index 080ea6614de0..940597760ee2 100644
>> --- a/src/libcamera/include/utils.h
>> +++ b/src/libcamera/include/utils.h
>> @@ -33,6 +33,7 @@ namespace utils {
>>  const char *basename(const char *path);
>>  
>>  char *secure_getenv(const char *name);
>> +std::string dirname(const std::string &path);
>>  
>>  template<class InputIt1, class InputIt2>
>>  unsigned int set_overlap(InputIt1 first1, InputIt1 last1,
>> diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
>> index 453e3b3b5995..f566e88cec5b 100644
>> --- a/src/libcamera/utils.cpp
>> +++ b/src/libcamera/utils.cpp
>> @@ -70,6 +70,54 @@ char *secure_getenv(const char *name)
>>  #endif
>>  }
>>  
>> +/**
>> + * \brief Identify the dirname portion of a path
>> + * \param[in] path The full path to parse
>> + *
>> + * This function conforms with the behaviour of the %dirname() function as
>> + * defined by POSIX.
>> + *
>> + * \return A string of the directory component of the path
>> + */
>> +std::string dirname(const std::string &path)
>> +{
>> +	if (path.empty())
>> +		return ".";
>> +
>> +	/*
>> +	 * Skip all trailing slashes. If the path is only made of slashes,
>> +	 * return "/".
>> +	 */
>> +	size_t pos = path.size() - 1;
>> +	while (path[pos] == '/') {
>> +		if (!pos)
>> +			return "/";
>> +		pos--;
>> +	}
>> +
>> +	/*
>> +	 * Find the previous slash. If the path contains no non-trailing slash,
>> +	 * return ".".
>> +	 */
>> +	while (path[pos] != '/') {
>> +		if (!pos)
>> +			return ".";
>> +		pos--;
>> +	}
>> +
>> +	/*
>> +	 * Return the directory name up to (but not including) any trailing
>> +	 * slash. If this would result in an empty string, return "/".
>> +	 */
>> +	while (path[pos] == '/') {
>> +		if (!pos)
>> +			return "/";
>> +		pos--;
>> +	}
>> +
>> +	return path.substr(0, pos + 1);
>> +}
>> +
>>  /**
>>   * \fn libcamera::utils::set_overlap(InputIt1 first1, InputIt1 last1,
>>   *				     InputIt2 first2, InputIt2 last2)
>> diff --git a/test/utils.cpp b/test/utils.cpp
>> index db1fbdde847d..e4184e39ce32 100644
>> --- a/test/utils.cpp
>> +++ b/test/utils.cpp
>> @@ -19,6 +19,56 @@ using namespace libcamera;
>>  class UtilsTest : public Test
>>  {
>>  protected:
>> +	int testDirname()
>> +	{
>> +		std::vector<std::string> paths = {
> 
> You can make this const.

Ah yes

> 
>> +			"",
>> +			"///",
>> +			"/bin",
>> +			"/usr/bin",
>> +			"//etc////",
>> +			"//tmp//d//",
>> +			"current_file",
>> +			"./current_file",
>> +			"./current_dir/",
>> +			"current_dir/",
>> +		};
>> +
>> +		std::vector<std::string> expected = {
> 
> And this too.
> 
>> +			".",
>> +			"/",
>> +			"/",
>> +			"/usr",
>> +			"/",
>> +			"//tmp",
>> +			".",
>> +			".",
>> +			".",
>> +			".",
>> +		};
> 
> I was going to propose using the libc-provided dirname() function to
> check for correctness instead of hardcoding the results, but that would
> create a dependency on the libc implementation conforming with POSIX,
> which may not be guaranteed on non-glibc platforms. Let's thus keep it
> as-is.

Indeed, I wanted direct expected results hardcoded for validation.

I also liked your utils::split test implementation, and I was tempted to
make this a vector of struct { char * path, char * expected } , but I
just went with this for speed, and because it's nice to show the whole
results in the event of a failure.


>> +
>> +		std::vector<std::string> results;
>> +
>> +		for (const auto &path : paths)
>> +			results.push_back(utils::dirname(path));
>> +
>> +		if (results != expected) {
>> +			cerr << "utils::dirname() tests failed" << endl;
>> +
>> +			cerr << "expected: " << endl;
>> +			for (const auto &path : expected)
>> +				cerr << " " << path << endl;
> 
> "\t" instead of " " for additional clarity ?

Sure, Mostly this code came in useful while I was fixing the
implementation so I /hope/ it will never be broken, and thus this code
shouldn't ever print (unless someone extends the tss?)

> 
>> +
>> +			cerr << "results: " << endl;
>> +			for (const auto &path : results)
>> +				cerr << " " << path << endl;
> 
> Here too ?
> 
>> +
>> +			return TestFail;
>> +		}
>> +
>> +		return 0;
> 
> 		return TestPass;
> 
>> +	}
>> +
>>  	int run()
>>  	{
>>  		/* utils::hex() test. */
>> @@ -71,6 +121,10 @@ protected:
>>  			return TestFail;
>>  		}
>>  
>> +		/* utils::dirname() tests. */
>> +		if (testDirname())
> 
> 		if (testDirname() != TestPass)
> 
> ?


Yes, that would be better in case the TestPass/TestFail values ever gets
changed underneath...

> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>


Thanks, fixups applied.


> 
>> +			return TestFail;
>> +
>>  		return TestPass;
>>  	}
>>  };
> 

-- 
Regards
--
Kieran


More information about the libcamera-devel mailing list