一个神奇的Bug
目前项目是一个非常传统的Web应用,其中有个页面需要用户填写自己的个人信息,包括姓名和出生日期。
目前项目是一个非常传统的Web应用,其中有个页面需要用户填写自己的个人信息,包括姓名和出生日期。
IE下的测试 作为一个有追求的程序员,应该尽可能的远离Windows系统。不论从专业开发者的角度,还是仅仅作为最终用户从使用体验上来说,Windows都可以算是垃圾中的战斗机:没有shell、响应极慢(比如从开机到可用需要多久,再对比一下Mac下的体验)、大部分操作都强依赖于鼠标,没有对应的快捷键、各类病毒等等。 但是,最为一个职业的程序员,又很难绕开Windows这个猥琐而又事实上很现实的存在,毕竟Windows在非专业市场上的占有率还是不容小觑的。一般而言,开发人员可以很轻松的使用现代的操作系统,编辑器,开发工具完成实际的业务需求,这部分工作很可能占整个交付工作的40%,但是又不得不在多个浏览器(IE的各个版本)中花费另外的60%。 既然很难抛开,那么我们就需要想办法简化对其的使用,比如将Windows隔离为一个纯粹的测试环境(不安装任何其他的软件,并且一旦感染病毒之后可以快速恢复)。 将Windows安装到虚拟机中 使用工具将诸如下载镜像,安装系统,安装特定版本的IE等操作简化为一条命令 可以很容易的创建一个干净,纯粹,稳定的Windows环境 ievms正是这样一个工具,它提供安装了各种版本IE的Windows操作系统的镜像,支持IE6到IE11。默认的,用户可以安装从IE6到IE11的所有镜像,但是很可能你无须所有的环境,ievms也提供对应的参数来确保只下载某一个。 不过对于一个团队来讲,可以安装所有的镜像到团队的某台公共的机器上,供所有人来进行跨IE浏览器的各个版本的测试。 这些虚拟机镜像都是虚拟磁盘vmdk文件,因此你需要先安装VirtualBox。 安装ievms 安装ievms非常容易,只需要下载一个脚本即可: $ curl -s https://raw.githubusercontent.com/xdissent/ievms/master/ievms.sh -L github会将该请求重定向,所以加上-L参数来跳转到实际的地址。下载之后,执行该脚本: $ chmod +x ievms.sh $ ./ievms.sh 默认的ievms会下载所有的虚拟机镜像,可以通过参数IEVMS_VERSIONS来选择特定版本的虚拟机: $ ./ievms.sh IEVMS_VERSIONS="7 8 9" 当然,也可以将这些命令合并为一行命令: $ curl -s https://raw.githubusercontent.com/xdissent/ievms/master/ievms.sh -L | IEVMS_VERSIONS="7 8 9" bash 用法 安装之后,一个新的虚拟机会被添加到VirtualBox中,只需要启动这个虚拟机即可: 另外,在这个虚拟机中,可以很方便的连接到宿主机。比如在宿主机上的12306端口运行了某个Web应用,那么通过地址:http://10.0.2.2:12306 来访问这个应用。 注意: 由于是整个虚拟磁盘的形式发布,因此这些镜像的体积都非常大,所有的镜像安装之后,会占用37G的空间,对于任何一个开发机来说,这个尺寸过于庞大,但是对于整个团队来说,应该还是可以接受的。 官方给出的尺寸列表如下: $ du -ch * 11G IE10 - Win7-disk1.vmdk 11G IE11 - Win7-disk1.vmdk 1.5G IE6 - WinXP-disk1.vmdk 1.6G IE7 - WinXP-disk1.vmdk 1....
分支策略 本来准备整理一篇版本管理中,关于分支的维护策略。后来看到阮一峰老师的这篇文章,觉得非常清晰,这里给出一个链接供参考。 另外一个有意思的链接在这里,也可以一并参看。 本文就仅仅简单的描述一下,使用svn的命令行工具,如何具体完成合并的操作: 在Svn中合并分支 在svn中,要合并两个分支(通常是将某个分支b合并到trunk上,不过另一种模式下也可以将trunk合并到b上)非常简单,我们以一个简单的例子来说明其步骤。 比如我们要将trunk上的修改合并到分支b上,操作可以分为4步: 切换到分支b上(之前执行过svn co /path/branches/b之后的目录) 使用svn log --stop-on-copy命令得到该分支的最早版本号 使用svn merge --dry-run -rXXX:HEAD /path/trunk来预览合并列表 合并 在第二步中,一个典型的输出是这样的: $ svn log --stop-on-copy ... r231625 | juntao | 2014-07-10 13:33:36 +1000 (Thu, 10 Jul 2014) | 1 line Juntao Change the version in pom.xml ------------------------------------------------------------------------ r231623 | abruzzi | 2014-07-10 13:22:00 +1000 (Thu, 10 Jul 2014) | 1 line Spike on data structure of c-wifi, a workable prototype ------------------------------------------------------------------------ r231610 | juntao | 2014-07-10 12:29:01 +1000 (Thu, 10 Jul 2014) | 1 line Create a new branch for c-wifi ------------------------------------------------------------------------ 一旦有了这个修订号(231610),就可以开始合并了:...
我的第一个机器人 以我的视角拍摄的机器人 以机器人自身的视角拍摄的 虽然迟了4年,但是我终于完成了我的第一台机器人的安装和配置。 基于Arduino的Duemilanove开发板 可以躲开障碍物继续前进(有距离探测器,与障碍物的距离小于20cm时会自动转向) 4轮驱动 我回忆了一下,这块板子和外设是我2010年3月在昆明的时候买的,那段时间对硬件突然有了极高的热情,但是买了之后准备开始之际,发现了几点困难: 当时航空管制,电池不能空运,因此网购的小车没有电池 缺乏基本的硬件常识,四个电机如何接在两个电机端口上 不会焊接电机 没有买杜邦线,没有测试用的面包板 基于这些困难,又加上我当时在找工作,准备面试之类,机器人的制造就放了下来。结果一放就是4年,中途从昆明搬家回到西安,不过细心的孙曼思将那一大包的零部件都打包好带了回来,多亏她认真负责的态度,才让这个机器人的产生有了可能。 ThoughtWorks西安硬件小组 2013年,在ThoughtWorks的西安Office有了硬件小组,他们研究开源硬件,以及这些小芯片在实际环境中的应用。比如RCA的高亮,做出了一个“半自动”地移动上的story的产品。2014年Hackday的时候,CASA团队又做出了一个看房机器人Dora,dora可以在房间中移动,由于装置了一个摄像头,因此可以实时的看到它看到的东西,而且Dora有安装了无线模块,你可以在远程操控它并得到实时图像。 好了,这么多铺垫事实上是有意义的,如果没有西安Office的硬件小组,我的这个机器人的诞生可能又需要若干年。它给我提供了丰富的资源,比如: 有丰富硬件经验的张新宇,高亮,王超,可以让我很容易问问题并得到答案 张新宇帮我焊了一个电源的接头 王超提供了一个7.2V的电池 高亮的百宝箱中有一个9V电池盒 CASA团队桌子上开发Dora的一些下脚料 杜邦线,热缩管,电烙铁,松香,镊子,钳子,各种螺丝刀等等 4年 vs 4天 虽然拖的时间很长(4年),但是事实上加起来搭建它的所有时间差不多是4天。 单独测试URM模块 单独测试电机驱动L298N模块 我发现,其实硬件开发和软件一样,化分模块,小步前进,测试,持续集成等等概念可以很好的运用其中,而且效果极好,比如我的小车基本上可以划分为这样几个小功能的逐步实现: 测距模块的单独启动 测距模块控制一个LED(距离小于5cm,点亮一个LED) 电机的单独启动(正转,反转) 测距模块控制电机(距离小于5cm,转动电机) 多个电机联合(如何联线) 测距模块控制多个电机 当到达最后一步时,基本上所有要验证的功能都已经就绪了,只需要拼装在小车的底座上,进行集成测试即可。其实,当划分好任务之后,每一个小的任务都不会花费很多时间。 故障 当我兴高采烈的去掉USB线,加上一个7.2V的电池给整个系统时,URM37测距模块伴随着浓郁的电器焦味中烧坏了,就是因为一个跳线的连接不当。这个系统中,电机需要单独加电,但是由于我自己电气知识的匮乏,导致了这个悲剧的发生。 不过,我又在Office的CASA团队的桌子上发现了另外一个测距模块,不过这次只经过了15分钟就完成了替换掉了老板子,换上了新的测距模块。 #include <NewPing.h> #define TRIGGER_PIN 12 // 12端口 #define ECHO_PIN 11 // 11端口 #define MAX_DISTANCE 400 int ledpin = 13; NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); void setup() { Serial....
CGI的一些背景 Web在设计之初只是可以提供静态内容,用于诸如文档分享,论文引用这样的内容。但是很快人们就不满足于静态的内容了,根据UNIX系统的哲学,人们倾向于让不同的应用程序通过已有的机制(进程间通信如管道,UNIX域socket,以及TCP/IPsocket)连接起来。 在Web服务器,诸如Apache httpd中加入与外部应用程序的通信接口就显得非常自然了。CGI(Common Gateway Interface)即是在这种背景下被发明的。 基本来说,CGI可以是任何的可执行程序,可以是Shell脚本,二进制应用,或者其他的脚本(Python脚本,Ruby脚本等)。CGI的基本流程是这样: Apache接收到客户端的请求 通过传统的fork-exec机制启动外部应用程序(cgi程序) 将客户端的请求数据通过环境变量和重定向发送给外部应用(cgi程序) 将cgi程序产生的输出写回给客户端(浏览器) 停止cgi程序(kill) 配置Apache支持CGI 本文的所有示例都是在Mac OSX环境下编写和实验。 先创建一个cgi的运行目录/Users/jtqiu/Sites/cgi-bin/,然后创建一个空的文件echo.cgi: $ mkdir -p /Users/jtqiu/Sites/cgi-bin/ $ touch echo.cgi 在这个文件中,添加一小段python代码: #!/usr/bin/env python print("Content-Type: text/html\n\n") print("<b>Hello, World</b>\n") 修改文件的执行权限: $ chmod +x echo.cgi 这段python代码并无特别,如果在shell运行这个脚本,可以得到: Content-Type: text/html <b>Hello, World</b> 这个可执行文件将作为我们的第一个CGI脚本,完成了这一步,我们需要配置Apache来支持CGI,首先,在目录/etc/apache2/users/中创建一个文件,文件名就是你的用户名称,如jtqiu.conf。 $ sudo vi /etc/apache2/users/jtqiu.conf 添加以下配置,其中/Users/jtqiu/Sites/cgi-bin/目录就是所有cgi脚本所在的目录,在次配置中AddHandler cgi-script .cgi表示为所有后缀为cgi的添加cgi-script的Handler。 <Directory "/Users/jtqiu/Sites/cgi-bin/"> AddHandler cgi-script .cgi Options +ExecCGI </Directory> 然后重启apache: $ sudo apachectl restart 下面我们就通过curl来进行测试: curl -v http://localhost/~jtqiu/cgi-bin/echo.cgi 更进一步 传统的CGI脚本的生命周期很短,Web服务器在接收到一次请求之后,会fork出一个进程来执行CGI脚本,一旦请求完成,这个进程就会被终止。 我们可以设置一个超时来查看:...