qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

IOS操作系统的层次结构

  1、Core OS 核心层:包含Accelerate Framework、External Accessory Framework、Security Framework、System等几个框架,基本都是基于c语言的接口
  2、Core Services核心服务层:包含Address Book Framework、CFNetwork Framework、Core Data Framework、Core Foundation Framework、Core Location Framework、Core Media Framework、Core Telephony Framework、Event Kit Framework、Foundation Framework、Mobile Core Services Framework、Quick Look Framework、Store Kit Framework、System Configuration Framework、Block Objects、Grand Central Dispatch  、In App Purchase、Location Services、SQLite、XML Support等一些框架,也基本都是基于c语言的接口。
  3、Mediah媒体层:包含Core Graphics、Core Animation、OpenGL ES、Core Text、Image I/O、Assets Library Framework、Media Player Framework、AV Foundation、OpenAL、Core Audio Frameworks、Core Media等等
  4、 Cocoa Touch 触摸层:包括Address Book UI Framework、Event Kit UI Framework、Game Kit Framework、iAd Framework、Map Kit Framework、Message UI Framework、UIKit Framework等等,这一层基本都是基于 Objective-c的接口

posted @ 2014-06-10 09:50 顺其自然EVO 阅读(1411) | 评论 (0)编辑 收藏

ORACLE数据库的优化

CPU参数的调整
  CPU是服务器的一项重要资,服务器良好的工作状态是在工作高峰时CPU的使用在90%以上。如果空闲时间CPU使用率就在90%以上,说明服务器缺乏CPU资源,如果工作高峰时CPU使用率仍然恨低,说明服务器CPU资源还比较富余。
  使用操作相同命令可以看到CPU的使用情况,以般UNIX操作系统的服务器,可以使用sar–u命令查看CPU的使用率,NT操作系统的服务器,可以使用NT的性能管理器来查看CPU的使用率。
  数据库管理员可以通过查看v$sysstat(select*fromv$sysstat wherenamelike'CPU%')数据字典中“CPUused by this session”统计项得知ORACLE数据库使用的CPU时间,查看“OS Userlevel CPU time“统计项得知操作系统用户态的CPU时间,查看“OS Systemcall CPU time“统计项得知操作系统系统态下的CPU时间,操作系统总的CPU时间就是用户态和系统态时间之和,如果ORACLE数据库使用的CPU时间占操作系统总的CPU时间90%以上,说明服务器CPU基本上被ORACLE数据库使用着,这是合理,反之,说明服务器CPU被其它程序占用过多,ORACLE数据库无法得到更多的CPU时间。
  数据库管理员还可以通过查看v$sesstat数据字典来获得当前连接ORACLE数据库各个会话占用的CPU时间,从而得知什么会话耗用服务器CPU比较多。(select*fromv$sysstat orderbyvaluedesc)
  出现CPU资源不足的情况是很多的:SQL语句的重解析、低效率的SQL语句、锁冲突都会引起CPU资源不足。
  1、数据库管理员可以执行下述语句来查看SQL语句的解析情况:
  SELECT * FROM V$SYSSTAT
  WHERE NAME IN
  ('parse time cpu', 'parse time elapsed', 'parse count (hard)');
  这里parse time cpu是系统服务时间,parse timeelapsed是响应时间,用户等待时间
  waite time = parse time elapsed – parse time cpu
  由此可以得到用户SQL语句平均解析等待时间=waite time / parse count,这个平均等待时间应该接近于0,如果平均解析等待时间过长,数据库管理员可以通过下述语句
  SELECTSQL_TEXT, PARSE_CALLS, EXECUTIONS FROM V$SQLAREA
  ORDER BY PARSE_CALLS;
  来发现是什么SQL语句解析效率比较低。程序员可以优化这些语句,或者增加ORACLE参数SESSION_CACHED_CURSORS的值。
  2、数据库管理员还可以通过下述语句:
  SELECT BUFFER_GETS, EXECUTIONS, SQL_TEXT FROMV$SQLAREA;
  查看低效率的SQL语句,优化这些语句也有助于提高CPU的利用率。
  3、数据库管理员可以通过v$system_event(select*fromv$system_event where event like'_atch%';)数据字典中的”latchfree“统计项查看ORACLE数据库的冲突情况,如果没有冲突的话,latch free查询出来没有结果。如果冲突太大的话,数据库管理员可以降低spin_count参数值,来消除高的CPU使用率。

内存参数的调整
  内存参数的调整主要是指ORACLE数据库的系统全局区(SGA)的调整。SGA主要由三部分构成:共享池、数据缓冲区、日志缓冲区。
  1、   共享池由两部分构成:共享SQL区和数据字典缓冲区,共享SQL区是存放用户SQL命令的区域,数据字典缓冲区存放数据库运行的动态信息。数据库管理员通过执行下述语句:
  select (sum(pins -reloads)) / sum(pins) "Lib Cache" from v$librarycache;
  来查看共享SQL区的使用率。这个使用率应在90%以上,否则需要增加共享池的大小。数据库管理员还可以执行下述语句:
  select (sum(gets - getmisses - usage - fixed))/ sum(gets) "Row Cache" from v$rowcache;
  查看数据字典缓冲区的使用率,这个使用率也应该在90%以上,否则需要增加共享池的大小。
  2、   数据缓冲区.数据库管理员可以通过下述语句:
  SELECT name, value  FROM v$sysstat  WHERE name IN ('db block gets', 'consistentgets','physical reads');
  来查看数据库数据缓冲区的使用情况。查询出来的结果可以计算出来数据缓冲区的使用命中率=1 - ( physical reads / (db block gets + consistent gets))。
  这个命中率应该在90%以上,否则需要增加数据缓冲区的大小。
  3、   日志缓冲区.数据库管理员可以通过执行下述语句:
  select name,value from v$sysstat where name in('redo entries','redo log space requests');查看日志缓冲区的使用情况。查询出的结果可以计算出日志缓冲区的申请失败率:
  申请失败率=requests/entries,申请失败率应该接近于0,否则说明日志缓冲区开设太小,需要增加ORACLE数据库的日志缓冲区。

posted @ 2014-06-10 09:49 顺其自然EVO 阅读(177) | 评论 (0)编辑 收藏

单元测试—使用模拟对象做交互测试

  最近在看.net单元测试艺术,我也喜欢单元测试,这里写一下如何在测试中使用模拟对象。
  开发的过程中,我们都会遇到对象间的依赖,比如依赖数据库或文件,这时,我们需要使用模拟对象,来进行测试,我们可以手写模拟对象,当然也可以使用模拟框架。
  假如有这样的一个需求,当用户登陆时,我需要对用户名和密码进行验证,然后再将用户名写入日志中。
public class MyLogin
{
public ILog Log { get; set; }
public bool Valid(string userName, string passWord)
{
var isValid = userName == "admin" && passWord == "123456";
Log.Write(userName);
return isValid;
}
}
public interface ILog
{
void Write(string message);
}
  上面的代码在验证完登陆信息后,需要向日志中写入用户名,由于写入日志可能依赖于文件或数据库,我们可能很难进行测试,所以,这里使用模拟对象进行测试。手写模拟对象,代码如下:
public class MyLoginTest
{
[Test]
public void Vaild_Test()
{
MyLogin login = new MyLogin();
var log = new TestLog();
login.Log = log;
var userNmae = "admin";
var passWord = "123456";
var isLogin = login.Valid(userNmae, passWord);
Assert.AreEqual(isLogin, true);
Assert.AreEqual(log.Message, userNmae);
}
}
public class TestLog : ILog
{
public string Message;
public void Write(string message)
{
this.Message = message;
}
}
  这里我们定义了一个对象TestLog对象,该对象就是一个模拟对像,继承了ILog接口。该测试中,一共进行了两项测试。一项是:验证用户名和密码是否输入正确。另一项是:验证用户写入日志的信息是否正确(比如应该写入用户名,结果把密码写入了日志,测试会无法通过)。
  这里我们区分一下模拟对象与桩对象。上一节中,我们讲过桩对象的定义,那么模拟对象与桩对象是什么关系呢?
  模拟对象与桩对象在写法上区别很小,关键在于模拟对象需要进行断言,也就是说模拟对象可以导致测试失败。桩对象只是为了方便测试所定义的一个对象,不需要进行断言,所以,桩对象永远不会导致测试失败。
  上面的测试中,如果我们去掉最后一行代码,即我们不进行写入日志的断言,则该对象就是一个桩对象。
  Assert.AreEqual(log.Message, userNmae);
  上面的模拟对象是我们自己写的,自己写模拟对象比较费时,我们可以使用模拟框架进行编写。这里我使用了Rhino Mocks框架。如果要执行下面的代码,需要下载Rhino.Mocks.dll文件,然后直接引用即可。
  测试框架这里我选用了NUnit框架。测试代码如下:
[TestFixture]
public class MyLoginTest
{
[Test]
public void Mock_Vaild_Test()
{
MockRepository mock = new MockRepository();
var log = mock.DynamicMock<ILog>();
var userName = "admin";
var passWord = "123456";
using (mock.Record())
{
log.Write(userName);
}
MyLogin login = new MyLogin();
login.Log = log;
var isLogin = login.Valid(userName, passWord);
Assert.AreEqual(isLogin, true);
mock.VerifyAll();
}
  这里我没有编写一个类去继承ILog接口,而是通过模拟框架,动态生成了一个ILog对象。代码是这句:
  MockRepository mock = new MockRepository();
  var log = mock.DynamicMock<ILog>();
  这里便生成了Log对象。通过录制-回放的模式进行模拟对象测试,首先需要定义我们的期望行为,最后验证实际行为与期望行为是否一致。这里,需要录制我们期望行为,代码如下:
using (mock.Record())
{
log.Write(userName);
}
  这里我们期望向日志中写入用户名。再通过回放来进行验证,代码如下:
  mock.VerifyAll();
  该方法会验证,期望向日志中写入的信息与实际向日志中写入的信息是否一致,如果不一致,测试失败。
  这里我们便完成了使用模拟框架进行单元测试。如果我们不需要测试日志写入方法,则把模拟对象换成桩对象就可以了,生成桩对象的方法如下:
  MockRepository mock = new MockRepository();
  var log = mock.Stub<ILog>();
  把回放的方法(mock.VerifyAll())去掉,就完成了模拟对象向桩对象的转变。注意,这里录制的代码还是需要的。
  总结:编写模拟对象和桩对象是非常有意义的,使用框架可以帮助我们简化单元测试。一般情况下,一个测试中,可以有多个桩对象,但最好只有一个模拟对象。模拟对象太多,证明一个测试方法做了太多项测试,不利于维护测试代码,一旦代码变改,很容易使单元测试失败。
  下一节,写一下测试框架的一些常用功能,如:如何模拟异常、如何模拟返回值等。。。

posted @ 2014-06-10 09:48 顺其自然EVO 阅读(270) | 评论 (0)编辑 收藏

支付宝分布式事务测试方案

  传统的基于数据库本地事务的解决方案只能保障单个服务的一次处理具备原子性、隔离性、一致性与持久性,但无法保障多个分布服务间处理的一致性。因此,我们必须建立一套分布式服务处理之间的协调机制,保障分布式服务处理的原子性、隔离性、一致性与持久性。
  支付宝为什么需要分布式事务
  基于SOA架构,整个支付宝系统会拆分成一系列独立开发、自包含、自主运行的业务服务,并将这些服务通过各种机制灵活地组装成最终用户所需要的产品与解决方案。
  在多个服务协同完成一次业务时,由于业务约束(如红包不符合使用条件、账户余额不足等)、系统故障(如网络或系统超时或中断、数据库约束不满足等),都可能造成服务处理过程在任何一步无法继续,使数据处于不一致的状态,产生严重的业务后果,所以我们需要一个分布式事务的解决方案,用来协调多个服务的业务一致性。
  支付宝的分布式事务框架
  支付宝开发的分布式事务是基于两阶段提交的理论(Two Phase Commit),首先给出两阶段提交的逻辑图:
  为了能够有效的让框架进行分布式事务的提交、回滚等动作,框架需要在整个两阶段执行过程中记录下足够的信息,设计了两张表来记录相关信息:
  分布式业务控制活动主表:记录了全局事务的活动状态;
  原子业务活动表:记录了原子业务活动的状态;
  我们用一个例子来说明:
  看一个典型的分布式事务场景。
  业务场景描述: 用户购买商品,使用支付宝余额支付;
  测试方案
  分析步骤
  角色定位
  各分支的业务活动记录状态
  梳理业务各个场景
  验证梳理场景
  恢复&回查机制
  角色定位
  首先测试人员需要分析所测试的系统处于分布式事务中的哪一个环节中,是处于事务的发起者,还是事务的参与者,不同的角色的定位对于测试分析角度不同,主要有以下的区别:
  发起者:
  分为同库/异库模式,主要区分是控制全局事务状态的主事务记录是否持久化在自己系统的db中;
  参与者:
  分为本地/远程模式,主要区分是是否可以创建嵌套的分布式事务;
  各分支的业务活动记录状态
  主事务记录:
  根据业务场景的不同,主事务记录状态也会相应改变,主要的状态机变化如图所示,测试人员需要模拟业务场景来验证状态机的迁转是否正确;
  同库:初始状态:I;提交成功:C;提交失败:I
  异库:初始状态:U;提交成功:U;提交失败:U

 梳理&验证业务场景
  分析维度
  一阶段:预处理:成功/失败;
  二阶段:提交/回滚;
  预期结果
  各个状态场景
  恢复&回查
  恢复:应用使用分布式事务,出现处理失败的业务活动,为了确保产生的影响不破坏业务一致性,我们必须对这些记录进行恢复处理
  回查:对于异库模式,事务状态为U,若提交或回滚失败,分布式事务总控系统无法感知这笔分布式事务是否执行成功,需要业务系统提供相应的回查接口;
  恢复及回查接口需要特别关注,对于分布式事务的正常二阶段提交或回滚,业务场景覆盖时多半都能check到,但是对于恢复及回查逻辑,很多时候都会遗漏,所以测试人员需要对这块特别做一个分析

posted @ 2014-06-10 09:47 顺其自然EVO 阅读(340) | 评论 (0)编辑 收藏

并发性能测试程序编写

 一般要测试软件或者库的性能,需要在多线程条件下进行。本文提供一种编写多线程性能测试的模板,方便大家参考和使用。
  本文以AES加密和解密为例,并指出Cipher的获取在程序中的不同位置会对程序性能造成的影响。
  程序代码如下:
package com.lazycat.secure.aes;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
public class AESCoder {
public static int count = 1000000;
public static CountDownLatch latch =new CountDownLatch(count);
public static void main(String[] args)throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(200);
final byte[] payload = "{\"msg\":{\"content\":{\"text\":\"JJH\",\"tplId\":0},\"from\":{\"name\":\"10000213\",\"id\":1,\"type\":0},\"to\":{\"name\":\"10095812\",\"id\":10000213,\"type\":0},\"time\":0,\"txid\":0,\"subtype\":1},\"type\":\"chat\"}".getBytes(Charset.forName("utf-8"));
final String secureKey ="BBmFdTFVgAjgHNwRkWWRcOFFiBzAANFU9DmMAP1JpBmc.";
long start = System.currentTimeMillis();
for(int i = 0 ; i <count ; i++){
pool.execute(new Runnable() {
public void run() {
AESCoder coder = new AESCoder();
byte[] enret =null;
try {
enret = coder.encrypt(payload,secureKey);
byte[] deret = coder.decrypt(enret, secureKey);
System.out.println(new String(deret,"utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
latch.countDown();
}
});
}
latch.await();
System.out.println(System.currentTimeMillis() - start);
pool.shutdown();
}
private byte[] encrypt(byte[] payload,String securekey)throws Exception{
byte[] enCodeFormat = securekey.substring(0, 16).getBytes();
SecretKeySpec key = newSecretKeySpec(enCodeFormat,"AES");
Cipher cipher = CliperInstance.getInstance();//创建密码器
//Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);//初始化
byte[] result = cipher.doFinal(payload);
return result;
}
public byte[] decrypt(byte[] buffer,StringsecureKey)throws Exception{
byte[] enCodeFormat = secureKey.substring(0,16).getBytes();
SecretKeySpec key = newSecretKeySpec(enCodeFormat,"AES");
//Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//创建密码器
Cipher cipher = CliperInstance.getInstance();
cipher.init(Cipher.DECRYPT_MODE, key);//初始化
byte[] result = cipher.doFinal(buffer);
return result;
}
}
class CliperInstance {
private static ThreadLocal<Cipher> cipherTL =new ThreadLocal<Cipher>(){
@Override
protected Cipher initialValue() {
try {
return Cipher.getInstance("AES/ECB/PKCS5Padding");
}catch(Exception e){
return null;
}
}
};
public static   CiphergetInstance() throws NoSuchAlgorithmException, NoSuchPaddingException{
returncipherTL.get();
}
}
 上述代码中有两个方法,分别是encrypt和decryption,分别表示加密和解密。
  为了简单,可以被多个线程共用,因此将CountDownLatch定义为static。
  在main函数中,创建了固定大小的线程池(200个线程,这个可以根据需要进行调整)和用于加密的payload和秘钥secureKey。Start用于记录开始时间,通过最后的System.currentTimeMillis()-start即可获得程序的运行时间。在for循环中,并发执行了count(10W)次payload的加密和解密,每个线程在执行完一个任务后会调用latch.countDown(),而主程序在循环后使用latch.await(),等待所有线程执行结束后,统计执行时间,输出消耗时间,最后关闭线程池。
  一般在做对比或者寻找瓶颈时,才会使用性能测试,下面给出一个性能对比的例子。
  在前面的代码中,无论是encrypt,还是decrypt,都有如下内容:
  Cipher cipher = CliperInstance.getInstance();//创建密码器
  //Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
  现在我们要对比每次加密和解密都执行Cipher.getInstance方法和对每一个线程自己都维护一个Cipher实例的性能差别。(我们都知道,Ciper.getInstance的调用时很耗时的)。
  使用Ciphercipher = CliperInstance.getInstance(); 时,执行100W次加密和解密,大约使用11.2s,而使用Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 大约需要39.6s,因此在进行加密、解密、签名等算法时,最好每个线程维护一个加密、解密或者签名的实例(这些实例是不能再多线程间共享的,如上例所示,调用init和doFinal必须在一个线程内完成,如果全局共享同一个实例,A调用init,B也调用了init,当A调用doFinal时,此时实例内的数据已经不是A的了,会出现异常)。
  顺便说一下,如果要求每个线程有自己的实例的情况下(如上面的加密和解密等),那么就可以使用ThreadLocal,在使用ThreadLocal时,重写内部的initialValue 方法,每次调用ThreadLocal的get方法时,ThreadLocal实例先到自己的Map中寻找有没有当前线程对应的Instance,如果存在,将Instance返回,如果不存在,调用initialValue去创建一个Instance,并将新Instance放到Map中后,返回。详情参看JDK文档。
  需要注意的是,CliperInstance 没必要非点单写成一个类,这里是为了让代码更容易懂。

posted @ 2014-06-10 09:46 顺其自然EVO 阅读(475) | 评论 (0)编辑 收藏

Android自动测试之monkeyrunner工具

前言:
  最近开始研究Android自动化测试方法,对其中的一些工具、方法和框架做了一些简单的整理,其中包括android测试框架、CTS、Monkey、Monkeyrunner、benchmark、其它test tool等等。因接触时间很短,很多地方有不足之处,希望能和大家多多交流。
  一、什么是monkeyrunner
  monkeyrunner工具提供了一个API,使用此API写出的程序可以在Android代码之外控制Android设备和模拟器。通过monkeyrunner,您可以写出一个Python程序去安装一个Android应用程序或测试包,运行它,向它发送模拟击键,截取它的用户界面图片,并将截图存储于工作站上。monkeyrunner工具的主要设计目的是用于测试功能/框架水平上的应用程序和设备,或用于运行单元测试套件,但您当然也可以将其用于其它目的。
  二、monkeyrunner工具同Monkey工具的差别
  Monkey:
  Monkey工具直接运行在设备或模拟器的adb shell中,生成用户或系统的伪随机事件流。
  monkeyrunner:
  monkeyrunner工具则是在工作站上通过API定义的特定命令和事件控制设备或模拟器。
  三、monkeyrunner的测试类型
  1、多设备控制:monkeyrunner API可以跨多个设备或模拟器实施测试套件。您可以在同一时间接上所有的设备或一次启动全部模拟器(或统统一起),依据程序依次连接到每一个,然后运行一个或多个测试。您也可以用程序启动一个配置好的模拟器,运行一个或多个测试,然后关闭模拟器。
  2、 功能测试: monkeyrunner可以为一个应用自动贯彻一次功能测试。您提供按键或触摸事件的输入数值,然后观察输出结果的截屏。
  3、 回归测试:monkeyrunner可以运行某个应用,并将其结果截屏与既定已知正确的结果截屏相比较,以此测试应用的稳定性。
  4、 可扩展的自动化:由于monkeyrunner是一个API工具包,您可以基于Python模块和程序开发一整套系统,以此来控制Android设备。除了使用monkeyrunner API之外,您还可以使用标准的Python os和subprocess模块来调用Android Debug Bridge这样的Android工具。
  四、运行monkeyrunner
  您可以直接使用一个代码文件运行monkeyrunner,抑或在交互式对话中输入monkeyrunner语句。不论使用哪种方式,您都需要调用SDK目录的tools子目录下的monkeyrunner命令。如果您提供一个文件名作为运行参数,则monkeyrunner将视文件内容为Python程序,并加以运行;否则,它将提供一个交互对话环境。
  monkeyrunner的命令语法为:
  monkeyrunner -plugin <plugin_jar> <program_filename> <program_options>
  五、实例
  以sample中的ApiDemos为例,先将其生成ApiDemos.apk。
  前提:已有device连接
  1、 将ApiDemos.apk放在$Android_Root\tools下。
  2、 在$Android_Root\tools下新建一个monkeyrunnerprogram.py文件,里面内容为:
1 # Imports the monkeyrunner modules used by this program
2
3  from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
4
5  # Connects to the current device, returning a MonkeyDevice object
6
7 device = MonkeyRunner.waitForConnection()
8
9  # Installs the Android package. Notice that this method returns a boolean, so you can test
10
11  # to see if the installation worked.
12
13 device.installPackage('./ApiDemos.apk')
14
15
16  # Runs the component
17
18 device.startActivity(component='com.example.android.apis/.ApiDemos')
19
20
21  # Presses the Menu button
22
23 device.press('KEYCODE_MENU','DOWN_AND_UP')
24
25
26  # Takes a screenshot
27
28 result = device.takeSnapshot()
29
30
31  # Writes the screenshot to a file
32
33 result.writeToFile('./shot1.png','png')
 注意:SDK上的例子有些错误,不可直接复制,否则执行命令时会发生错误。具体可与我的上面这段代码对照。
  3、 打开命令行转到Android_Root\tools目录下运行一下命令:
monkeyrunner monkeyrunnerprogram.py
110307 15:33:19.625:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: wake.
110307 15:33:20.625:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: wake.
110307 15:33:21.625:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: wake.
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] Error starting command: monkey --port 12345
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]com.android.ddmlib.ShellCommandUnresponsiveException
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at com.android.ddmlib.AdbHelper.executeRemoteCommand(AdbHelper.java:408)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at com.android.ddmlib.Device.executeShellCommand(Device.java:276)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at com.android.monkeyrunner.adb.AdbMonkeyDevice$1.run(AdbMonkeyDevice.java:89)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.util.concurrent.Executors$RunnableAdapter.call(UnknownSource)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.util.concurrent.FutureTask.run(Unknown Source)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.util.concurrent.ThreadPoolExecutor$Worker.run(UnknownSource)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.lang.Thread.run(UnknownSource)
110307 15:33:57.437:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: press KEYCODE_MENU.
110307 15:33:59.171:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: quit.
  注:里面exception的提示我们可以忽略,因为我们可以看见 Monkey Command: press KEYCODE_MENU已经执行成功。
  4、 可以Android_Root\tools下查看生成的shot1.png的截图。
  六、实例扩展
  因为ApiDemos首页上按下MENU键没有菜单出现,为了更加形象化,在实例五的基础上继续试验:
  1、 在$Android_Root\tools下新建一个monkeyrunnerprogram1.py文件,里面内容为:
1 # Imports the monkeyrunner modules used by this program
2
3  from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
4
5 # Connects to the current device, returning a MonkeyDevice object
6
7 device = MonkeyRunner.waitForConnection()
8
9 # Takes a screenshot
10
11 result = device.takeSnapshot()
12
13 # Writes the screenshot to a file
14
15 result.writeToFile('./shotbegin.png','png')
16
17 # Presses the Down button
18
19 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
20
21 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
22
23 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
24
25 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
26
27 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
28
29 # Takes a screenshot
30
31 result = device.takeSnapshot()
32
33 # Writes the screenshot to a file
34
35 result.writeToFile('./shotend.png','png')
  2、  将画面定位在Apidemos的首页,并将光标定位在第一项上。
  3、  在$Android_Root\tools目录下运行一下命令:
  monkeyrunner monkeyrunnerprogram1.py
  4、在运行过程中我们可以看见光标不断向下移动,并且可以在当前目录下我们自定义的截图:
  运行前:
  运行后(做了五次下移操作):

posted @ 2014-06-10 09:45 顺其自然EVO 阅读(179) | 评论 (0)编辑 收藏

Java中RMI远程调用

 Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。
  Java RMI极大地依赖于接口。在需要创建一个远程对象的时候,程序员通过传递一个接口来隐藏底层的实现细节。客户端得到的远程对象句柄正好与本地的根代码连接,由后者负责透过网络通信。这样一来,程序员只需关心如何通过自己的接口句柄发送消息。
  服务端新建接口:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RmiTestInterface extends Remote{
public String getTest() throws RemoteException;
}
  接口的实现:
import java.rmi.RemoteException;
public class RmiTestImpl implements RmiTestInterface {
public RmiTestImpl() throws RemoteException {
//super();
// TODO Auto-generated constructor stub
//UnicastRemoteObject.exportObject(this);
}
/**
*
*/
public String getTest() throws RemoteException {
// TODO Auto-generated method stub
return "Hello,Test";
}
}
  定义一个main方法,注册你已经实现的RMI接口,包括开放端口等:
public static void main(String []args) throws AlreadyBoundException, RemoteException{
RmiTestImpl t=new RmiTestImpl();
RmiTestInterface tt=(RmiTestInterface)UnicastRemoteObject.exportObject(t,0);
// Bind the remote object's stub in the registry
Registry registry = LocateRegistry.createRegistry(2001);
registry.rebind("test", tt);
System.out.println("server is start");
}
 Server端的代码已经全部写完,但是还要把接口类(RmiTestInterface)打包成jar,导入进client端的项目中。
  运行服务端后控制台输出:
  server is start
  导入服务端的接口jar以后,可以开始编写一个client端的主程序:
public static void main(String []args){
try {
Registry registry = LocateRegistry.getRegistry("localhost",2001);
RmiTestInterface t= (RmiTestInterface) registry.lookup("test");
System.out.println("client:"+t.getTest());
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotBoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
  运行客户端main方法后,控制台输出:
  Hello,Test
English »
 
English »
 

posted @ 2014-06-09 10:36 顺其自然EVO 阅读(149) | 评论 (0)编辑 收藏

MySQL数据库如何做好优化

 碰到过好几次这样的题目,可每次都不能答的完全正确,现在大概列出如下:(望各位补充)
  1.数据库的设计
  (尽量把数据库设计的更小的占磁盘空间。
  (1).尽可能使用更小的整数类型。(mediumint就比int更合适)。
  (2).尽可能的定义字段为not null,除非这个字段需要null。
  (3).如果没有用到变长字段的话比如varchar,那就采用固定大小的纪录格式比如char。
  (4).表的主索引应该尽可能的短,这样的话每条纪录都有名字标志且更高效。
  (5).只创建确实需要的索引。索引有利于检索记录,但是不利于快速保存记录。如果总是要在表的组合字段上做搜索,那么就在这些字段上创建索引。索引的第一部分必须是最常使用的字段。如果总是需要用到很多字段,首先就应该多复制这些字段,使索引更好的压缩。
  (6).所有数据都得在保存到数据库前进行处理。
  (7).所有字段都得有默认值。
  (8).在某些情况下,把一个频繁扫描的表分成两个速度会快好多。在对动态格式表扫描以取得相关记录时,它可能使用更小的静态格式表的情况下更是如此。
  2.系统的用途
  (1).尽量使用长连接。
  (2).explain 复杂的SQL语句。
  (3).如果两个关联表要做比较话,做比较的字段必须类型和长度都一致。
  (4).LIMIT语句尽量要跟 order by 或者 distinct。这样可以避免做一次full table scan。
  (5).如果想要清空表的所有纪录,建议用 truncate table tablename 而不是 delete from tablename。
  (6).能使用 STORE PROCEDURE 或者 USER FUNCTION的时候。
  (7).在一条insert语句中采用多重纪录插入格式,而且使用 load data infile 来导入大量数据,这比单纯的indert快好多。
  (8).经常 OPTIMIZE TABLE 来整理碎片。
  (9).还有就是 date 类型的数据如果频繁要做比较的话尽量保存在 unsigned int 类型比较快。
  3.系统的瓶颈
  (1).磁盘搜索
  并行搜索,把数据分开存放到多个磁盘中,这样能加快搜索时间.
  (2).磁盘读写(IO)
  可以从多个媒介中并行的读取数据。
  (3).CPU周期
  数据存放在主内存中,这样就得增加CPU的个数来处理这些数据。
  (4).内存带宽
  当CPU要将更多的数据存放到CPU的缓存中来的话,内存的带宽就成了瓶颈.
English »
 

posted @ 2014-06-09 10:34 顺其自然EVO 阅读(171) | 评论 (0)编辑 收藏

Android自动测试之monkeyrunner工具

 前言:
  最近开始研究Android自动化测试方法,对其中的一些工具、方法和框架做了一些简单的整理,其中包括android测试框架、CTS、Monkey、Monkeyrunner、benchmark、其它test tool等等。因接触时间很短,很多地方有不足之处,希望能和大家多多交流。
  一、什么是monkeyrunner
  monkeyrunner工具提供了一个API,使用此API写出的程序可以在Android代码之外控制Android设备和模拟器。通过monkeyrunner,您可以写出一个Python程序去安装一个Android应用程序或测试包,运行它,向它发送模拟击键,截取它的用户界面图片,并将截图存储于工作站上。monkeyrunner工具的主要设计目的是用于测试功能/框架水平上的应用程序和设备,或用于运行单元测试套件,但您当然也可以将其用于其它目的。
  二、monkeyrunner工具同Monkey工具的差别
  Monkey:
  Monkey工具直接运行在设备或模拟器的adb shell中,生成用户或系统的伪随机事件流。
  monkeyrunner:
  monkeyrunner工具则是在工作站上通过API定义的特定命令和事件控制设备或模拟器。
  三、monkeyrunner的测试类型
  1、多设备控制:monkeyrunner API可以跨多个设备或模拟器实施测试套件。您可以在同一时间接上所有的设备或一次启动全部模拟器(或统统一起),依据程序依次连接到每一个,然后运行一个或多个测试。您也可以用程序启动一个配置好的模拟器,运行一个或多个测试,然后关闭模拟器。
  2、 功能测试: monkeyrunner可以为一个应用自动贯彻一次功能测试。您提供按键或触摸事件的输入数值,然后观察输出结果的截屏。
  3、 回归测试:monkeyrunner可以运行某个应用,并将其结果截屏与既定已知正确的结果截屏相比较,以此测试应用的稳定性。
  4、 可扩展的自动化:由于monkeyrunner是一个API工具包,您可以基于Python模块和程序开发一整套系统,以此来控制Android设备。除了使用monkeyrunner API之外,您还可以使用标准的Python os和subprocess模块来调用Android Debug Bridge这样的Android工具。
  四、运行monkeyrunner
  您可以直接使用一个代码文件运行monkeyrunner,抑或在交互式对话中输入monkeyrunner语句。不论使用哪种方式,您都需要调用SDK目录的tools子目录下的monkeyrunner命令。如果您提供一个文件名作为运行参数,则monkeyrunner将视文件内容为Python程序,并加以运行;否则,它将提供一个交互对话环境。
  monkeyrunner的命令语法为:
  monkeyrunner -plugin <plugin_jar> <program_filename> <program_options>
  五、实例
  以sample中的ApiDemos为例,先将其生成ApiDemos.apk。
  前提:已有device连接
  1、 将ApiDemos.apk放在$Android_Root\tools下。
  2、 在$Android_Root\tools下新建一个monkeyrunnerprogram.py文件,里面内容为:
1 # Imports the monkeyrunner modules used by this program
2
3  from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
4
5  # Connects to the current device, returning a MonkeyDevice object
6
7 device = MonkeyRunner.waitForConnection()
8
9  # Installs the Android package. Notice that this method returns a boolean, so you can test
10
11  # to see if the installation worked.
12
13 device.installPackage('./ApiDemos.apk')
14
15
16  # Runs the component
17
18 device.startActivity(component='com.example.android.apis/.ApiDemos')
19
20
21  # Presses the Menu button
22
23 device.press('KEYCODE_MENU','DOWN_AND_UP')
24
25
26  # Takes a screenshot
27
28 result = device.takeSnapshot()
29
30
31  # Writes the screenshot to a file
32
33 result.writeToFile('./shot1.png','png')
 注意:SDK上的例子有些错误,不可直接复制,否则执行命令时会发生错误。具体可与我的上面这段代码对照。
  3、 打开命令行转到Android_Root\tools目录下运行一下命令:
monkeyrunner monkeyrunnerprogram.py
110307 15:33:19.625:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: wake.
110307 15:33:20.625:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: wake.
110307 15:33:21.625:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: wake.
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] Error starting command: monkey --port 12345
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]com.android.ddmlib.ShellCommandUnresponsiveException
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at com.android.ddmlib.AdbHelper.executeRemoteCommand(AdbHelper.java:408)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at com.android.ddmlib.Device.executeShellCommand(Device.java:276)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at com.android.monkeyrunner.adb.AdbMonkeyDevice$1.run(AdbMonkeyDevice.java:89)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.util.concurrent.Executors$RunnableAdapter.call(UnknownSource)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.util.concurrent.FutureTask.run(Unknown Source)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.util.concurrent.ThreadPoolExecutor$Worker.run(UnknownSource)
110307 15:33:22.718:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]  at java.lang.Thread.run(UnknownSource)
110307 15:33:57.437:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: press KEYCODE_MENU.
110307 15:33:59.171:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: quit.
  注:里面exception的提示我们可以忽略,因为我们可以看见 Monkey Command: press KEYCODE_MENU已经执行成功。
  4、 可以Android_Root\tools下查看生成的shot1.png的截图。
  六、实例扩展
  因为ApiDemos首页上按下MENU键没有菜单出现,为了更加形象化,在实例五的基础上继续试验:
  1、 在$Android_Root\tools下新建一个monkeyrunnerprogram1.py文件,里面内容为:
1 # Imports the monkeyrunner modules used by this program
2
3  from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
4
5 # Connects to the current device, returning a MonkeyDevice object
6
7 device = MonkeyRunner.waitForConnection()
8
9 # Takes a screenshot
10
11 result = device.takeSnapshot()
12
13 # Writes the screenshot to a file
14
15 result.writeToFile('./shotbegin.png','png')
16
17 # Presses the Down button
18
19 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
20
21 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
22
23 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
24
25 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
26
27 device.press('KEYCODE_DPAD_DOWN','DOWN_AND_UP')
28
29 # Takes a screenshot
30
31 result = device.takeSnapshot()
32
33 # Writes the screenshot to a file
34
35 result.writeToFile('./shotend.png','png')
  2、  将画面定位在Apidemos的首页,并将光标定位在第一项上。
  3、  在$Android_Root\tools目录下运行一下命令:
  monkeyrunner monkeyrunnerprogram1.py
  4、在运行过程中我们可以看见光标不断向下移动,并且可以在当前目录下我们自定义的截图:
  运行前:
  运行后(做了五次下移操作):

posted @ 2014-06-09 10:28 顺其自然EVO 阅读(159) | 评论 (0)编辑 收藏

Sonar 代码质量管理平台

1) 下载
  从sonar官网http://www.sonarsource.org/下载 (版本当然是最新的了)
  在官网上是不分系统的,一个zip包,下下来之后,包里包含 windows 、linux 和mac的版本
  2)解压 执行
  我用的是 sonarqube-4.1
  解压出这么一个文件夹后,打开bin/macosx-universal-64 ( 注: 我的系统是mac 64位的,所以选的这个目录 )
  ./sonar.sh start
  出现下面字样,说明运行成功
  Starting sonar...
  Started sonar.
  打开浏览器,http://localhost:9000 看一看,是不是出来了? 简单吧。 当然这还没完,还要配置一下数据库
  3)配置数据库
  我用的是mysql
  新建一个名为 sonar 的数据库 ,注意编码为utf8(库名不喜欢的话,可以随便改,要conf中对应上就可以  ),表就不需要管了。
  4) 修改配置文件
  位置 conf/sonar.properties
  主要就是修改 连接数据库的用户名、密码、连接方式
  5)修改maven的配置文件 settings.xml
  添加以下内容:
<profiles>
<profile>
<id>sonar</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<!-- EXAMPLE FOR MYSQL -->
<sonar.jdbc.url>
jdbc:mysql://127.0.0.1:3306/sonar?characterEncoding=utf8&amp;useUnicode=true&amp;autoReconnect=true&amp;failOverReadOnly=false
</sonar.jdbc.url>
<sonar.jdbc.driverClassName>com.mysql.jdbc.Driver</sonar.jdbc.driverClassName>
<sonar.jdbc.username>root</sonar.jdbc.username>
<sonar.jdbc.password>******</sonar.jdbc.password>
<!-- SERVER ON A REMOTE HOST -->
<sonar.host.url>http://127.0.0.1:9000</sonar.host.url> <!-- 这个地址是你 sonar服务器的地址 -->
</properties>
</profile>
</profiles>
  6) 基础设施搭好了,开始测试
  进入你的项目运行:
  mvn clean install
  mvn sonar:sonar
  运行完之后,打开 localhost:9000 看一下吧,是不是倒进去了呢。
  7)问题
  如果出现问题,一定要耐心的解决,很简单,都不是问题。

posted @ 2014-06-09 10:18 顺其自然EVO 阅读(189) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 102 103 104 105 106 107 108 109 110 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜