Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Ignore messages that matches a given pattern #502

Closed
wants to merge 3 commits into from

6 participants

@jivagoalves

cc @jferris

This PR adds a new feature so that the user will be able to ignore messages from output by using a pattern. This is useful to get rid of annoying messages.

For example, after running the following code:

# ignore_pattern.rb

$LOAD_PATH.unshift "/vagrant/lib"
require "capybara"
require "capybara-webkit"

html = %(<html><body style="font-size: 0px">Hello World</body></html>)
app = proc { |env| [200, { "Content-Type" => "text/html" }, [html] ] }

sess = Capybara::Session.new(:webkit, app)
sess.visit("/")

You should see a warning "QFont::setPixelSize: Pixel size <= 0":

vagrant@precise32:/vagrant$ ruby ignore_pattern.rb 
QFont::setPixelSize: Pixel size <= 0 (0)
vagrant@precise32:/vagrant$

If you ask the driver to ignore it:

# ignore_pattern.rb

$LOAD_PATH.unshift "/vagrant/lib"
require "capybara"
require "capybara-webkit"

html = %(<html><body style="font-size: 0px">Hello World</body></html>)
app = proc { |env| [200, { "Content-Type" => "text/html" }, [html] ] }

sess = Capybara::Session.new(:webkit, app)
sess.driver.ignore("QFont::setPixelSize: Pixel size <= 0")
sess.visit("/")

Then you shouldn't see any QFont::setPixelSize: Pixel size <= 0 from the output:

vagrant@precise32:/vagrant$ ruby ignore_pattern.rb 
vagrant@precise32:/vagrant$ 
spec/connection_spec.rb
@@ -32,6 +32,31 @@
io.string.should =~ /hello world $/
end
+ it 'ignores by a pattern' do
@jferris Admin
jferris added a note

I think this would be easier to read/maintain as a Browser spec, because you can then rely on Browser to send/receive the command/response.

Sure, I'll try to come up with a better Browser spec instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
spec/driver_spec.rb
@@ -2225,6 +2225,48 @@ def log
end
end
+ describe "ignoring messages" do
@jferris Admin
jferris added a note

If we have a good Browser or Connection spec in place for this, I don't think we need to add a driver spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/IgnoreMessage.cpp
((4 lines not shown))
+#include "WebPageManager.h"
+#include <QRegExp>
+
+IgnoreMessage::IgnoreMessage(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
+}
+
+void IgnoreMessage::start() {
+ patterns << arguments()[0];
+ qInstallMsgHandler(messageHandler);
+ finish(true);
+}
+
+QStringList IgnoreMessage::patterns = QStringList();
+
+void IgnoreMessage::messageHandler(QtMsgType type, const char *msg) {
+ bool foundMatch = false;
@jferris Admin
jferris added a note

I think extracting a shouldPrintMessage method from this would clarify this code.

Good point. I'm extracting it out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jferris
Admin

Thanks a bunch. We've wanted this feature for quite a while.

I had a few comments that I made inline, and I have one high-level question: what do you think about ignoring a few messages by default? For example, I'm assuming that "QFont" message isn't helpful to anybody using capybara-webkit, and it would save time and stress if it were ignored by default.

@mhoran thoughts?

@jivagoalves

Hey @jferris , thanks for reviewing. I think it's okay to ignore at least the well known message QFont::setPixelSize: Pixel size <= 0 (0). Since I don't know if there are other helpful QFont messages, I think it should be up to the users to ignore them. Unless you're pretty sure they're all worthless. Would you mind to list some messages you think it's not helpful?

@jivagoalves

@jferris I pushed some changes you've suggested:

  • More smaller methods to clarify the code
  • Browser spec instead

We can squash the commits later on when we agree everything is ready to be merged. Please let me know if there is anything we could improve. Thanks!

@mhoran
Collaborator

I wish there were a way to know the source of the message so we could just silence internal Qt errors. I definitely think we should have some default ignored messages, given all the open issues regarding debug output. But capturing those and keeping a list up to date is going to be tedious.

What if instead we install a message handler that ignores all debug and warning messages and instead of writing to qDebug from the logger, write to a QDebug instance that's tied to stderr? Then we get full control, and silence the internal errors without the need for a blacklist.

Another option would be a whitelist of messages instead of blacklist.

@jferris
Admin

If we could silence all internal errors in some way without keeping a list, that would certainly be nice.

Disabling the default qDebug instance would be a little annoying when we're actually debugging things, but I don't think it would be a deal-breaker.

We should also consider how we want console.log to behave; that's a useful debugging tool for users, but there are jQuery plugins and other 3rd party libraries that use console.log when it's defined. A number of users have complained about facebook messages in the output.

I think the changes here would be useful even if we could silence the internal Qt errors, given the issues with console.log.

@mhoran
Collaborator

As of 2ceab4e, console.log is written to the logger, so it will only appear on stderr when the debug driver is used.

@jivagoalves

@mhoran, would we have something like the following then?

void myMessageOutput(QtMsgType type, const char *msg)
 {
     switch (type) {
     case QtDebugMsg:
     case QtWarningMsg:
         break;
     default:
         fprintf(stderr, "%s\n", msg);
         break;
     }
 }

 int main(int argc, char **argv)
 {
     qInstallMsgHandler(myMessageOutput);
     QApplication app(argc, argv);
     ...
     return app.exec();
 }

I couldn't find how to return a QDebug instance tied to stderr. Can you please provide me some example/docs about that?

Besides that, I was thinking we could use two message handlers: one for ignoring debugs and warnings messages (the default handler above) and other to print to stderr. Therefore we would have to switch between them from the logger just to print at the moment we want. I'm not sure if that is the best solution though. What do you guys think?

@jferris
Admin

I think you can get a QDebug instance that writes to stderr with this:

QFile stderr;
stderr.open(STDERR)
QDebug debug(stderr);
@jivagoalves

Hey guys, sorry for the late response.

I've been trying to come up with a solution using a QDebug instance that writes to stderr without success.

// src/main.cpp
void messageHandler(QtMsgType type, const char *msg) {
  switch (type) {
    case QtDebugMsg:
    case QtWarningMsg:
      break;
    default:
      fprintf(stderr, "%s\n", msg);
      break;
  }
}

int main(int argc, char **argv) {
  qInstallMsgHandler(messageHandler);
  ...
}
// src/WebPageManager.cpp
QDebug WebPageManager::logger() const {
  if (m_loggingEnabled && m_stderrOutput->open(stderr, QIODevice::Append)) {
    return QDebug(m_stderrOutput);
  }
  else {
    return QDebug(m_ignoredOutput);
  }
}

After some testing it looks like I can't open the stderr file descriptor:

  2) Capybara::Webkit::Driver logger app logs its commands after turning on the logger
     Failure/Error: log.should include logging_message
       expected "" to include "Wrote response true"
     # ./spec/driver_spec.rb:2209:in `block (3 levels) in <top (required)>'

  3) Capybara::Webkit::Connection forwards stderr to the given IO object
     Failure/Error: io.string.should =~ /hello world $/
       expected: /hello world $/
            got: "" (using =~)
       Diff:
       @@ -1,2 +1 @@
       -/hello world $/
     # ./spec/connection_spec.rb:32:in `block (2 levels) in <top (required)>'

I did try another approach by switching the message handler when enabling the logger like the following:

// src/main.cpp
void messageHandler(QtMsgType type, const char *msg) {
  switch (type) {
    case QtDebugMsg:
    case QtWarningMsg:
      break;
    default:
      fprintf(stderr, "%s\n", msg);
      break;
  }
}

int main(int argc, char **argv) {
  qInstallMsgHandler(messageHandler);
  ...
}

and

// src/WebPageManager.cpp
void loggingMessageHandler(QtMsgType type, const char *msg) {
  fprintf(stderr, "%s\n", msg);
}

QDebug WebPageManager::logger() const {
  if (m_loggingEnabled) {
    qInstallMsgHandler(loggingMessageHandler);
    return qDebug();
  }
  else {
    return QDebug(m_ignoredOutput);
  }
}

That seems to work, but I don't know if it's a good solution though. What do you guys think? Do you know a better approach?

@jferris
Admin

Thanks for looking into this. A couple notes:

  • I'm not sure if qInstallMsgHandler applies just to the default qDebug instance, or to all QDebug instances globally.
  • The logger instance there is only used by capybara-webkit, and not by internal Qt messages. The warnings we're trying to silence will be writing to qDebug() whether or not m_loggingEnabled is true, so I think you want to install that handler unconditionally when capybara-webkit starts.

If you're having trouble with the approach I recommended, you could try out @mhoran's other idea: use a whitelist. Here's how I think it could work:

  • I think we only produce output when using logger. Any other output is from Qt.
  • Can we prefix our output with "capybara-webkit" somehow?
  • If so, we can install a message handler that ignores any messages that don't begin with "capybara-webkit."
@iainbeeston

Any updates on this?

@brookr

This would be super helpful for cleaning up test output... Warning messages are confusing to my students, and make them more likely to not read console output. :[

@jferris
Admin

@jivagoalves thanks for all your work on this. Because of the delay here, I ended up implementing this. Closing in favor of #586.

@jferris jferris closed this
@kris-luminar

If anyone finds this thread and is still having problems with these warning messages in OS X Mavericks, please see: #581 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
6 README.md
@@ -223,6 +223,12 @@ page.driver.console_messages.first[:message]
page.driver.header 'Referer', 'https://www.thoughtbot.com'
```
+**ignore**: ignore messages that matches a pattern
+
+```ruby
+page.driver.ignore("QFont::setPixelSize: Pixel size <= 0")
+```
+
Contributing
------------
View
4 lib/capybara/webkit/browser.rb
@@ -14,6 +14,10 @@ def enable_logging
command "EnableLogging"
end
+ def ignore(pattern)
+ command("IgnoreMessage", pattern)
+ end
+
def visit(url)
command "Visit", url
end
View
4 lib/capybara/webkit/driver.rb
@@ -21,6 +21,10 @@ def enable_logging
browser.enable_logging
end
+ def ignore(pattern)
+ browser.ignore(pattern)
+ end
+
def current_url
browser.current_url
end
View
72 spec/browser_spec.rb
@@ -258,4 +258,76 @@
expect { browser.visit("/") }.not_to raise_error(/empty response/)
end
+
+ describe "#ignore" do
+ before(:each) do
+ # set up minimal HTTP server
+ @host = "127.0.0.1"
+ @server = TCPServer.new(@host, 0)
+ @port = @server.addr[1]
+
+ @server_thread = Thread.new do
+ while conn = @server.accept
+ Thread.new(conn) do |conn|
+ # write response
+ html = <<-HTML
+ <html>
+ <head>
+ <style>
+ body { font-size: 0px; }
+ </style>
+ </head>
+ <body></body>
+ </html>
+ HTML
+ conn.write "HTTP/1.1 200 OK\r\n"
+ conn.write "Content-Type:text/html\r\n"
+ conn.write "Content-Length: %i\r\n" % html.size
+ conn.write "\r\n"
+ conn.write html
+ conn.write("\r\n\r\n")
+ conn.close
+ end
+ end
+ end
+ end
+
+ after(:each) do
+ @server_thread.kill
+ @server.close
+ end
+
+ let(:output) { StringIO.new }
+
+ def log
+ output.rewind
+ output.read
+ end
+
+ let(:browser) do
+ connection = Capybara::Webkit::Connection.new(:stderr => output)
+ Capybara::Webkit::Browser.new(connection)
+ end
+
+ let(:logging_messages) do
+ [
+ "Wrote response true",
+ %(Started request to "http://127.0.0.1:#{@port}/")
+ ]
+ end
+
+ it "does not ignore by default" do
+ browser.enable_logging
+ browser.visit("http://#{@host}:#{@port}/")
+ logging_messages.each { |msg| log.should include msg }
+ end
+
+ it "ignores by a given pattern" do
+ browser.ignore("http://\\d+\.\\d+\.\\d+\.\\d+")
+ browser.ignore("Wrote .*")
+ browser.enable_logging
+ browser.visit("http://#{@host}:#{@port}/")
+ logging_messages.each { |msg| log.should_not include msg }
+ end
+ end
end
View
1  src/CommandFactory.cpp
@@ -40,6 +40,7 @@
#include "Version.h"
#include "Title.h"
#include "FindCss.h"
+#include "IgnoreMessage.h"
CommandFactory::CommandFactory(WebPageManager *manager, QObject *parent) : QObject(parent) {
m_manager = manager;
View
38 src/IgnoreMessage.cpp
@@ -0,0 +1,38 @@
+#include "IgnoreMessage.h"
+#include "SocketCommand.h"
+#include "WebPage.h"
+#include "WebPageManager.h"
+#include <QRegExp>
+
+IgnoreMessage::IgnoreMessage(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
+}
+
+void IgnoreMessage::start() {
+ patterns << arguments()[0];
+ qInstallMsgHandler(messageHandler);
+ finish(true);
+}
+
+QStringList IgnoreMessage::patterns = QStringList();
+
+void IgnoreMessage::messageHandler(QtMsgType type, const char *msg) {
+ if (shouldPrintMessage(msg)) {
+ fprintf(stderr, "%s\n", msg);
+ }
+}
+
+bool IgnoreMessage::shouldPrintMessage(const char *msg) {
+ for (int i = 0; i < patterns.size(); ++i) {
+ if (matchesPattern(patterns.at(i), msg)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool IgnoreMessage::matchesPattern(QString pattern, const char *msg) {
+ QRegExp regex(pattern);
+
+ return regex.indexIn(msg) != -1;
+}
View
17 src/IgnoreMessage.h
@@ -0,0 +1,17 @@
+#include "SocketCommand.h"
+#include <QStringList>
+
+class IgnoreMessage : public SocketCommand {
+ Q_OBJECT
+
+ public:
+ IgnoreMessage(WebPageManager *, QStringList &arguments, QObject *parent = 0);
+ virtual void start();
+
+ private:
+ static QStringList patterns;
+ static void messageHandler(QtMsgType type, const char *msg);
+ static bool shouldPrintMessage(const char *msg);
+ static bool matchesPattern(QString pattern, const char *msg);
+};
+
View
1  src/find_command.h
@@ -42,3 +42,4 @@ CHECK_COMMAND(SetUrlBlacklist)
CHECK_COMMAND(Title)
CHECK_COMMAND(Version)
CHECK_COMMAND(FindCss)
+CHECK_COMMAND(IgnoreMessage)
View
2  src/webkit_server.pro
@@ -2,6 +2,7 @@ TEMPLATE = app
TARGET = webkit_server
DESTDIR = .
HEADERS = \
+ IgnoreMessage.h \
Version.h \
EnableLogging.h \
Authenticate.h \
@@ -65,6 +66,7 @@ HEADERS = \
NetworkReplyProxy.h
SOURCES = \
+ IgnoreMessage.cpp \
Version.cpp \
EnableLogging.cpp \
Authenticate.cpp \
Something went wrong with that request. Please try again.