GMOCK下对const函数重载的处理

在C++中我们可能会遇到函数名, 函数签名, 返回值都相同的重载, 唯一的区别是const和non-const, 例如:

1
2
3
4
5
6
class SomeClass {
public:
virtual ~SomeClass() throw() {}
virtual int get() = 0;
virtual int get() const = 0;
};

这种类我们在使用时编译器会根据我们的传入对象是否带有const属性自动调用对应的版本, 但是通过GMOCK的编写Mock调用时, 很多人对如何设置const函数的EXPECT_ALL和调用产生了疑问.
其实处理方式也比较简单, 在使用EXPECT_CALL或者调用mock对象的方法时, 我们可以显示通过const_cast将mock对象的转为const版本.
例如对于上面的类, 我们可以这么去写Mock, 设置期望和调用:

handle ambiguous overloaded functions in gmock

在声明的接口中, 可能会存在返回值及函数参数个数一直的情况, 例如下面Inf::f()方法:

1
2
3
4
5
6
7
8
#include "gmock/gmock.h"
class Inf {
public:
virtual ~Inf() throw() {}
virtual void f(const ::std::string &name, ::std::string &value) = 0;
virtual void f(const ::std::string &name, long &value) = 0;
};

调用时传入对应类型的参数可以匹配到对应签名的方法, 对其编写Mock也没有任何问题:

1
2
3
4
5
6
class InfMock {
public:
virtual ~InfMock() throw() {}
MOCK_CONST_METHOD2(f, void(const ::std::string &name, ::std::string &value));
MOCK_CONST_METHOD2(f, void(const ::std::string &name, long &value));
};

但是实际调用时, 向第二个有歧义的参数设置值时, EXPECT_CALL接受Mock方法时通过Any是没有办法区分出期望调用的是哪个函数, 此时可以通过::testing::An来传入具体类型以匹配对应签名的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TEST(GMock, ShouldAbleToIdentifyOverloadedFunctions) {
InfMock infmock;
EXPECT_CALL(infmock, f("hello", ::testing::An<::std::string &>()))
.WillOnce(::testing::SetArgReferee<1>("9527"));
EXPECT_CALL(infmock, f("world", ::testing::An<long &>()))
.WillOnce(::testing::SetArgReferee<1>(134));
::std::string strVal;
infmock.f("hello", strVal);
ASSERT_EQ("9527", strVal);
long longVal;
infmock.f("world", longVal);
ASSERT_EQ(134, longVal);
}

移除HTTP Proxy的NTLM认证

某些企业内网处于安全考虑, 封堵了互联网访问, 外网出口仅保留一个HTTP Proxy, 在国内不少企业会架设AD并使用ISA作为代理服务器及防火墙.

通过ISA支持的NTLM认证能够比较方便的实现Windows端的SSO, 但是这种明显Windows风的认证方式却有很多HTTP客户端不支持.

例如在某客户现场使用npm通过代理安装包时, npm无法通过NTLM认证导致安装失败. 此时我们可以选择CNTLM将代理转化为不需要认证的HTTP代理.

首先从CNTLM官方网站下载最新版本的CNTLM程序:

http://sourceforge.net/projects/cntlm/files/

C++ 源码阅读项目推荐

Chromium

  • 流行浏览器的Chrome的开发版本
  • 源码符合Google C++ 编程风格规范Google C++ Style Guide
  • 较多人进行阅读, 有一些现有阅读笔记Chrome源码剖析
  • 能够学习到比较全面各种子系统的设计知识, 如网络, 多线程, IO, 内存管理等
  • 其项目中包含了若干Google的子项目, 也很值得阅读, 例如: libv8, gtest, gflags
  • 源码地址: https://code.google.com/p/chromium/

STL

  • C++标准模板库, 日常开发必备
  • 通过源码能够学习到模板编程基础, 相对于Boost来说可读性很高, 上手容易
  • 通过学习STL实现能够更好的掌握STL的开发范式, 例如通过迭代器编程而不是传递容器, 使用或扩展STL中的算法
  • 通过了解STL常用容器的底层实现也能更好的让我们再不同场景下选择合适的容器, 避免因误用造成时间空间的浪费
  • STL实现版本较多, 内部接口的编程风格不是很好, 建议通过SGI STL的源码进行阅读
  • 源码地址: https://www.sgi.com/tech/stl/

禁止Visual Assist自动添加Override关键字

问题描述

Visual Assist是一款历史悠久的Visual Studio插件, 能够极大的提升C++开发效率. 但是某些时候智能过了头, 反倒成了开发效率的绊脚石, 例如在实现虚方法(Implement Virtual Methods)时自动添加的override关键字.

1
2
3
4
class SomeInterface {
public:
virtual void doSomething() = 0
};

使用Implement Virtual Methods后, Visual Assist帮我们生成如下代码:

1
2
3
4
5
6
7
class SomeImplementation : public SomeInterface {
public:
virtual void doSomething() override
{
throw std::logic_error("The method or operation is not implemented.");
}
};

Visual Assist在virtual void doSomething()后面帮我们添加了一个override关键字, 这个关键是符合c++11标准的. 但是一些老旧的编译器并不支持override, 因此导致编译错误. 这使我们不得不手动删除所有生成方法后的override关键字.

使用Mockcpp Mock C++静态函数

传统测试中的Mock, 都是基于多态实现的, 也就是Mock面向接口的虚函数. 但是在C++的代码中, 经常会混入大量的C函数或是静态成员函数.
例如工厂函数, 单例函数, 或是C库中的函数甚至STL的算法等.

对于这些静态函数, 比较传统的做法是创建一个Wrapper, 用虚方法对这些静态函数进行包裹. 在测试的时候对Wrapper进行Mock便可控制被包裹的静态函数的行为:

1
int add(int x, int y);

可以通过Wrapper包裹为:

1
2
3
4
5
6
class Calc {
public:
virtual int add(int x, int y) {
return ::add(x, y);
}
}

但是对于存量代码, 这种重构并不现实(工作量及流程问题). 正当我们束手无策时, 我们发现mockcpp可以帮助我们解决一部分静态方法Mock的需求.

VC中对SEH的扩展

这篇文章是早前跟某同事探讨Windows SEH中__finally实现时研究的内容, 根据某书介绍, 异常处理函数都是通过_EXCEPTION_REGISTRATION_RECORD内的回调函数Handler实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
__try
{
...
}
__except( XXX )
{
// 这里是对应那个_EXCEPTION_REGISTRATION_RECORD 的第二个成员Handler
...
}

但是对于__finally却没有介绍具体的实现方式. 通过调试器trace不到__finally的调用路径.

古董街机修复之路

加入Thoughtworks前参观西安Office时就已经注意到办公室角落的一台街机, 在入职后才知道此街机已经坏了有些年月. 机缘巧合, 加入硬件小组后了解到有修复游戏机的计划, 所以便有了以下的经历.

方案选择

开搞之前, 新宇和高亮已经把游戏机肚里的东西摸得一清二楚. 游戏机主板寄给某个淘宝店家后已杳无音讯, 屏幕则是一块接受RGBS信号的25寸显像管, 摇杆和按钮是最直接的按键开关, 闭合后会跟GND连通. 有了这些基本输入输出信息, 下一步就是选择合适的软硬件来重建主板.

回想曾经的街机经历, 无非也就两种选择: 一是到游戏厅玩真机, 第二种便是通过PC上的模拟器. 重买一个街机主板放进去肯定不是我们想要的方案, 所以我们基本确定思路就是通过PC主机+模拟器的方式. 比较常见的街机模拟器有winkawaks, nebula和MAME等, 相信不少人都有玩过:

MAME

防止Ubuntu启动时自动进入GUI

为了实验几个需要X的小程序, 很不情愿的在Ubuntu Server上安装了Ubuntu-Desktop, 但是Ubuntu每次引导后自动进入Gnome实在是烦.

一般情况下想默认进入CLI的话只需要修改下runlevel, 可是我安装的Ubuntu(Server 12.04 LTS)默认的runlevel是2, Gnome也跑在level 2上. 貌似不能像往常一样通过修改inittab来禁用X.

让mac下rvm支持更多的编译器

通过rvm安装ruby-1.9.3-p484时, 发现rvm会尝试通过brew安装gcc46, 但在编译gcc46依赖的ppl011时产生错误, 导致整个安装流程失败. 我的机器上已经通过brew安装了更高版本的gcc49, rvm看起来还不够聪明去自动使用更新的编译器.

从网上搜索解决方案时看到这个commit:

img