EXPECT_THROW does not catch exception with gcc 4.8.2

3,455 views
Skip to first unread message

gus

unread,
Feb 19, 2014, 10:16:52 AM2/19/14
to googletes...@googlegroups.com
The following piece of code:

TEST(MappedFile, Fail) {
  const char* bad_file = "/doesntexistsforsure/thatwouldbecrazy!";
  EXPECT_THROW(mapped_file mf(bad_file), jellyfish::mapped_file::ErrorMMap);
}

when compiled with gcc 4.8.2 on Debian does not run. It fails with the following error:

Note: Google Test filter = MappedFile.Fail
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from MappedFile
[ RUN      ] MappedFile.Fail
terminate called after throwing an instance of 'jellyfish::mapped_file::ErrorMMap'
  what():  Can't open file '/doesntexistsforsure/thatwouldbecrazy!': No such file or directory

The error thrown is the one intended but was not caught by gtest as expected. Earlier version of gcc did not have this problem.

Gus.

Billy Donahue

unread,
Feb 19, 2014, 5:28:13 PM2/19/14
to gus, Google C++ Testing Framework
Can you give a smaller test case?


--
 
---
You received this message because you are subscribed to the Google Groups "Google C++ Testing Framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to googletestframe...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Roger Leigh

unread,
Mar 27, 2014, 5:18:02 PM3/27/14
to googletes...@googlegroups.com
I'm seeing the same thing on MacOSX 10.9 with clang++3.4 and Linux with GCC 4.8.2.  Exceptions are unhandled and the test program terminates immediately.  A little more investigation shows that this is because the exceptions aren't of the correct type (possibly) and/or aren't being caught correctly.  I've attempted to make a reduced testcase below.  However, it's not perfect.  The problematic test in my own code is:

  // Invalid file
  EXPECT_THROW(r2.isUsedFile("unused-nonexistent-file"), boost::filesystem::filesystem_error);
  // Valid but unused file
  EXPECT_THROW(r2.isUsedFile(PROJECT_SOURCE_DIR "/components/specification/samples/2012-06/18x24y5z5t2c8b-text.ome"), std::logic_error);

The lines doing the throwing in each case are, respectively:

path thisfile = boost::filesystem::canonical(path(file));
throw std::logic_error(msg);

The boost::filesystem::canonical function is documented to throw a boost::filesystem::filesystem_error when invoked in this manner.  The logic_error is a plain std::logic_error.  Yet ASSERT_THROW and EXPECT_THROW fail to catch *both*.  However, gtest might not necessarily be at fault here.  Adding in a few asserts in a try/catch block shows that the boost::filesystem::filesystem_error isn't caught, but a std::runtime_error *is*.  However... if I compile a simpler example, the program terminates with the message:

#include <iostream>
#include <stdexcept>
#include <boost/filesystem/operations.hpp>

namespace
{
  void test_throw()
  { throw std::logic_error("Bad logic"); }
}

int main()
{
  try
    {
      boost::filesystem::canonical(boost::filesystem::path("/invalid/path"));
    }
  catch (const boost::filesystem::filesystem_error& e)
    { std::cerr << "Caught FSE" << std::endl; }
  catch (const std::runtime_error& e)
    { std::cerr << "Caught RTE" << std::endl; }
  catch (...)
    { std::cerr << "Caught general exception" << std::endl; }
}

==> it hits the runtime_error catch block
Caught RTE
or
terminating with uncaught exception of type boost::filesystem::filesystem_error: boost::filesystem::canonical: No such file or directory: "/invalid/path"
if the try/catch is removed.  There's clearly something fundamentally wrong here.  It *is* throwing the correct exception.  But it's not being caught in the correct block and is falling through to be caught as a more generic type.
 
Minimal gtest test case below.  This is basically the same tests run via google test.  The observed behaviour is the same.  In the minimal test is appear to catch the logic_error correctly, but not the boost::filesystem::filesystem_error.  Link with "-lboost_system -lboost_filesystem".  You might need to comment out the various bits to see all the different tests in action since it'll abort on the earlier tests.

#include <stdexcept>
#include <boost/filesystem/operations.hpp>
#include <gtest/gtest.h>

namespace
{
  void test_throw()
  { throw std::logic_error("Bad logic"); }
}

TEST(ETest, Defaults)
{
  try
    {
      boost::filesystem::canonical(boost::filesystem::path("/invalid/path")); // test basic language-level exception throwing and handling
    }
  catch (const boost::filesystem::filesystem_error& e)
    {
      ASSERT_TRUE(true);
    }
  catch (const std::runtime_error& e)
    {
      ASSERT_TRUE(false);
    }
  catch (...)
    {
      ASSERT_TRUE(false);
    }

  // Invalid file
  EXPECT_THROW(boost::filesystem::canonical(boost::filesystem::path("/invalid/path")), boost::filesystem::filesystem_error);

  // Valid but unused file
  EXPECT_THROW(test_throw(), std::logic_error);
}

Roger Leigh

unread,
Mar 27, 2014, 5:19:48 PM3/27/14
to googletes...@googlegroups.com
Just for the record, I'm using gtest 1.7 built against the correct compiler version in both cases.

Roger Leigh

unread,
Mar 27, 2014, 6:19:55 PM3/27/14
to googletes...@googlegroups.com
A further note: I tested on MacOS X 10.9 with the default compiler (not homebrew clang++3.4)m on Debian unstable with GCC 4.8.2 and on FreeBSD with clang++ 3.3 and 3.4.  They all worked flawlessly.

I would suggest that the behaviour you are seeing is symptomatic of a broken and/or incompatible libstdc++/libc++.  For example, in the homebrew case, clang++-3.4 is linked against the system (3.4svn) version of libc++ and there is possibly some subtle incompatibility here.  Maybe the typeinfos for the types in the 3.4-compiled translation unit aren't being used by the 3.3 runtime when unwinding, or aren't being used correctly.

Roger Leigh

unread,
Mar 27, 2014, 7:18:26 PM3/27/14
to googletes...@googlegroups.com
Last thing from me today.  While using the standard MacOS X 10.9 clang++ (3.4svn) fixes the boost::filesystem::filesystem_error catching, it's still failing to catch std::logic_error.  It's not just missing the correct catch block, it's failing outright with

libc++abi.dylib: terminating with uncaught exception of type std::logic_error: .....

This appears to be completely independent of gtest, just mentioning it here for reference.  Throwing a logic_error in ASSERT_THROW works fine, so I've not yet got a reduced test case for this.
 

Roger Leigh

unread,
Mar 28, 2014, 10:25:33 AM3/28/14
to googletes...@googlegroups.com

This turned out to be a due to an exception being thrown inside a dtor during the unwind.  You'd have thought this would be obvious (and, indeed the FreeBSD gdb showed this up directly in the stack trace), but the MacOSX lldb debugger did not give any indication that this was the site of the error.  If the OP's toolchain and standard library have no compatibility issues, this might be worth checking, as would be making sure that no nothrow() or throws() restrictions are violated.
Reply all
Reply to author
Forward
0 new messages