<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tao</title>
	<atom:link href="http://www.theantway.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.theantway.com</link>
	<description>C/C++, Java, Python, Algorithm, Database ...</description>
	<lastBuildDate>Tue, 08 May 2012 01:38:47 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Use space instead of Tab in vim</title>
		<link>http://www.theantway.com/2012/05/08/use-space-instead-of-tab-in-vim/</link>
		<comments>http://www.theantway.com/2012/05/08/use-space-instead-of-tab-in-vim/#comments</comments>
		<pubDate>Tue, 08 May 2012 01:30:34 +0000</pubDate>
		<dc:creator>xu wei</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[vim]]></category>

		<guid isPermaLink="false">http://www.theantway.com/?p=209</guid>
		<description><![CDATA[Use the following settings in ~/.vimrc file: set expandtab &#34; use space instead of tab set tabstop=4 &#34; set tab width to 4 spaces set shiftwidth=4 &#34; auto indented with the reindent operations]]></description>
			<content:encoded><![CDATA[<p>Use the following settings in ~/.vimrc file:</p>
<pre class="brush: shell; toolbar: false">set expandtab &quot; use space instead of tab
set tabstop=4 &quot; set tab width to 4 spaces
set shiftwidth=4 &quot; auto indented with the reindent operations</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.theantway.com/2012/05/08/use-space-instead-of-tab-in-vim/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在集成测试环境中, 根据历史测试结果自动进行测试分配</title>
		<link>http://www.theantway.com/2012/04/13/assign_tests_on_jenkins_based_on_build_history-cn/</link>
		<comments>http://www.theantway.com/2012/04/13/assign_tests_on_jenkins_based_on_build_history-cn/#comments</comments>
		<pubDate>Fri, 13 Apr 2012 14:17:16 +0000</pubDate>
		<dc:creator>xu wei</dc:creator>
				<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[Functional Test]]></category>
		<category><![CDATA[Jenkins]]></category>

		<guid isPermaLink="false">http://www.theantway.com/?p=117</guid>
		<description><![CDATA[在我目前和以前的项目中，都遇到了测试运行时间太长，需要把测试分配到多台机器上并行运行的问题。有一些项目在手工的进行测试分配，而本文将介绍如何自动的进行测试分配，从而减少花在这些琐事上的时间。 最开始的问题是什么 随着项目的不断进行，测试会越来越多，而跑一次测试的时间也越来越长，尤其是如果界面测试多的话，那么测试运行时间很容易比较长。以我目前所在的项目为例，基于浏览器的界面测试（以下称为Functional Test）如果单机跑的话大约要80分钟。这样开发人员提交一次代码需要很长时间才能从CI Server上得到结果，而CI反馈周期过长会很大的降低开发人员对CI的关注度，即使仍然关注也会因为不断的切换上下文而降低工作效率。 初始的解决方案 开始时大家会用各种简单的办法来解决这个问题，比如通过tag或annotation(不同的测试框架有不同的概念)手工的把测试分成几部分，然后增加Build agent的数量，让每个Build agent 只跑一部分测试，理想的结果是每个Build agent的测试时间一样长，这样运行测试的时间在理想情况下就是：总的测试时间 / Build agent的数量 但这样同时也带来了新的问题：当增加测试时，设置或修改哪个测试在哪台机器上跑的工作是很琐碎的，我们需要经常关注每个Build agent当前的运行时间是多少, 新的测试加到哪个上面比较合适。我在工作中就经常听到有人说，现在测试太慢了，我们再加一个Build agent, 然后分点测试过去吧。简单重复劳动做的多了，就说明需要关注一下，看看有没有好的办法来解决。好，那就思考一下。 退一步，重新审视问题 每次代码提交后我们需要运行一些Functional Test, 因为测试本身运行时间太长，所以我们需要想办法减少测试时间。相对于换更快的机器来说，增加机器进行并行测试有更好的扩展性，并且成本较低，所以方向应该是没错的。既然方向没有问题，那现在要做的就是要用自动的方式来代替手工分配。而理想的分配方式就是根据时间来进行，这样可以尽量保证不同的机器运行测试的时间相差不大。 现在让我们来明确列出目前要解决的问题：找出当前需要运行的所有测试，根据他们的运行时间，分成几组，在几台机器上并行运行指定的测试，仔细考虑，可以分成以下几个部分： 找出所有需要运行的测试？嗯，还不清楚怎么做 每个测试的运行时间，这个是已知的，因为我们可以从CI上拿到历史测试数据。对于新增的测试，我们可以用平均的测试时间来估计。 分组的数目是确定的，有多少个Build agent 就分几组（配几个就有几个） 运行指定的测试，嗯，这个也得google一下 分组部分的要求是比较明确地，算法也比较简单输入：一个测试列表，Build agent的数量（分成几组，下面用n表示），历史测试时间处理：根据测试的运行时间分配输出：n组测试列表，每组测试的运行时间尽量相同 我们要做的事情： 下面整理一下我们的分析结果： 已知的东西： 分组的数目 测试的历史运行时间 需求明确，可以进行的： 根据测试时间进行自动分配（已完成，参看https://github.com/theantway/test-distributor） 需要进一步研究的： 找到所有要运行的测试 运行指定的一组测试 实际上，这两个需要进一步研究的内容需要根据不同的测试框架采用专门的方式来完成。 如果您要把本文介绍的自动分配测试的方式应用到您的CI环境，并且用的是测试框架与本文介绍的相同，那么您就可以直接用了，否则需要针对您使用的测试框架找出上面的最后两点。 使用脚本自动分配测试 通过读取历史运行结果，脚本test_distributor.py 可以把传入的测试列表分成 n份，并且生成n个文件。生成文件的文件名和目录位置都可以通过命令行参数进行配置，每个文件里包含分配好的测试列表。 $ python test_distributor.py --help usage: test_distributor.py [-h] &#8230; </p><p><a class="more-link block-button" href="http://www.theantway.com/2012/04/13/assign_tests_on_jenkins_based_on_build_history-cn/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p>在我目前和以前的项目中，都遇到了测试运行时间太长，需要把测试分配到多台机器上并行运行的问题。有一些项目在手工的进行测试分配，而本文将介绍如何自动的进行测试分配，从而减少花在这些琐事上的时间。</p>
<h2>最开始的问题是什么</h2>
<p>随着项目的不断进行，测试会越来越多，而跑一次测试的时间也越来越长，尤其是如果界面测试多的话，那么测试运行时间很容易比较长。以我目前所在的项目为例，基于浏览器的界面测试（以下称为Functional Test）如果单机跑的话大约要80分钟。这样开发人员提交一次代码需要很长时间才能从CI Server上得到结果，而CI反馈周期过长会很大的降低开发人员对CI的关注度，即使仍然关注也会因为不断的切换上下文而降低工作效率。</p>
<h2>初始的解决方案</h2>
<p>开始时大家会用各种简单的办法来解决这个问题，比如通过tag或annotation(不同的测试框架有不同的概念)手工的把测试分成几部分，然后增加Build agent的数量，让每个Build agent 只跑一部分测试，理想的结果是每个Build agent的测试时间一样长，这样运行测试的时间在理想情况下就是：总的测试时间 / Build agent的数量</p>
<p>但这样同时也带来了新的问题：当增加测试时，设置或修改哪个测试在哪台机器上跑的工作是很琐碎的，我们需要经常关注每个Build agent当前的运行时间是多少, 新的测试加到哪个上面比较合适。我在工作中就经常听到有人说，现在测试太慢了，我们再加一个Build agent, 然后分点测试过去吧。简单重复劳动做的多了，就说明需要关注一下，看看有没有好的办法来解决。好，那就思考一下。</p>
<h2>退一步，重新审视问题</h2>
<p>每次代码提交后我们需要运行一些Functional Test, 因为测试本身运行时间太长，所以我们需要想办法减少测试时间。相对于换更快的机器来说，增加机器进行并行测试有更好的扩展性，并且成本较低，所以方向应该是没错的。既然方向没有问题，那现在要做的就是要用自动的方式来代替手工分配。而理想的分配方式就是根据时间来进行，这样可以尽量保证不同的机器运行测试的时间相差不大。</p>
<p>现在让我们来明确列出目前要解决的问题：找出当前需要运行的所有测试，根据他们的运行时间，分成几组，在几台机器上并行运行指定的测试，仔细考虑，可以分成以下几个部分：</p>
<ul>
<li>找出所有需要运行的测试？嗯，还不清楚怎么做</li>
<li>每个测试的运行时间，这个是已知的，因为我们可以从CI上拿到历史测试数据。对于新增的测试，我们可以用平均的测试时间来估计。</li>
<li>分组的数目是确定的，有多少个Build agent 就分几组（配几个就有几个）</li>
<li>运行指定的测试，嗯，这个也得google一下</li>
<li>分组部分的要求是比较明确地，算法也比较简单输入：一个测试列表，Build agent的数量（分成几组，下面用n表示），历史测试时间处理：根据测试的运行时间分配输出：n组测试列表，每组测试的运行时间尽量相同</li>
</ul>
<h2>我们要做的事情：</h2>
<p>下面整理一下我们的分析结果：</p>
<p>已知的东西：</p>
<ul>
<li>分组的数目</li>
<li>测试的历史运行时间</li>
</ul>
<p>需求明确，可以进行的：</p>
<ul>
<li>根据测试时间进行自动分配（已完成，参看<a title="https://github.com/theantway/test-distributor" href="https://github.com/theantway/test-distributor">https://github.com/theantway/test-distributor</a>）</li>
</ul>
<p>需要进一步研究的：</p>
<ul>
<li>找到所有要运行的测试</li>
<li>运行指定的一组测试</li>
</ul>
<p>实际上，这两个需要进一步研究的内容需要根据不同的测试框架采用专门的方式来完成。</p>
<p>如果您要把本文介绍的自动分配测试的方式应用到您的CI环境，并且用的是测试框架与本文介绍的相同，那么您就可以直接用了，否则需要针对您使用的测试框架找出上面的最后两点。</p>
<h2>使用脚本自动分配测试</h2>
<p>通过读取历史运行结果，脚本test_distributor.py 可以把传入的测试列表分成 n份，并且生成n个文件。生成文件的文件名和目录位置都可以通过命令行参数进行配置，每个文件里包含分配好的测试列表。</p>
<pre class="brush: bash; toolbar: false">$ python test_distributor.py --help
usage: test_distributor.py [-h] -s [TESTS_HISTORY_REPORT_FOLDER] [-n [BLOCKS]]
                           [-p [RESULT_FILENAME_PREFIX]]
                           [-e [RESULT_FILENAME_EXTENSION]] [-d [DEST_FOLDER]]
                           [--delimiter [DELIMITER]]

Assign tests to n blocks for distributed tests.

optional arguments:
  -h, --help            show this help message and exit
  -s [TESTS_HISTORY_REPORT_FOLDER], --source-folder [TESTS_HISTORY_REPORT_FOLDER]
                        source folder which contains history tests reports
                        (default: None)
  -n [BLOCKS], --blocks [BLOCKS]
                        how many blocks to split (default: 1)
  -p [RESULT_FILENAME_PREFIX], --name-prefix [RESULT_FILENAME_PREFIX]
                        base name for the result files (default: tests)
  -e [RESULT_FILENAME_EXTENSION], --name-extension [RESULT_FILENAME_EXTENSION]
                        file extension for the result files (default: txt)
  -d [DEST_FOLDER], --dest-folder [DEST_FOLDER]
                        destination folder for generated files (default: test-
                        blocks)
  --delimiter [DELIMITER]
                        delimiter (default: ,)</pre>
<p>&nbsp;</p>
<p>例如，</p>
<p>在test-blocks目录下生成5个文件：tests-1.txt, tests-2.txt…tests-5.txt。</p>
<pre class="brush: bash; toolbar: false">$ less all_tests | python test_distributor.py –source-folder=HISTORY_REPORTS_FOLDER –-blocks=5</pre>
<p>上面的less all_tests是列出所有需要运行的测试.</p>
<h2>自动下载需要的文件</h2>
<p>为了在CI环境中方便的进行环境搭建，尽量减少手工操作，可以通过脚本自动的从github上下载需要的文件。curl可以在下载前比较远程文件有没有更新，只有当有了新版本的文件时，curl才会真正进行下载，这样下载花的时间也可以忽略：</p>
<pre class="brush: bash; toolbar: false">curl -z ~/test_distributor.py -L https://raw.github.com/theantway/test-distributor/master/test_distributor.py -o ~/test_distributor.py
curl -z ~/twist-tests-explorer-1.0.tar.gz -L https://github.com/downloads/theantway/test-distributor/twist-tests-explorer-1.0.tar.gz -o ~/twist-tests-explorer-1.0.tar.gz</pre>
<p>通过指定 –z file_name，可以告诉curl, 如果文件没有更改，就不要下载了。这个操作也可以通过wget来进行，这里使用curl是因为在Windows的git bash下自带了curl,而没有wget。</p>
<h2>找出Twist的所有测试</h2>
<p>如前文所述，需要针对不同的框架使用不同的方式得到测试列表，现在我们看怎样得到Twist的所有测试。</p>
<p>Twist本身并没有提供一个命令行选项来列出所有指定Tag的测试，但因为测试框架自己也需要找出满足条件的测试，于是通过Twist提供的库文件，我们可以比较方便的得到需要的信息。</p>
<p>相应代码在：<a title="https://github.com/theantway/test-distributor/tree/master/twist-tests-explorer" href="https://github.com/theantway/test-distributor/tree/master/twist-tests-explorer">https://github.com/theantway/test-distributor/tree/master/twist-tests-explorer</a>，</p>
<p>编译好的二进制文件在：<a title="https://github.com/theantway/test-distributor/downloads" href="https://github.com/theantway/test-distributor/downloads">https://github.com/theantway/test-distributor/downloads</a>, 下载后解压到test-distributor目录（您也可以解压到其他位置），使用方式是:</p>
<pre class="brush: bash; toolbar: false">$ java -jar test-distributor/twist-tests-explorer-1.0.jar -s FunctionalTests/scenarios -t \\&#039;TEAM-FR &amp; !State-In-Progress\\&#039;</pre>
<h2>运行指定的Twist测试</h2>
<p>Twist 本身提供了运行指定文件中所有测试的功能，使用方式是调用Twist时指定<span style="color: #000000;">scenarioListFile=file_name。</span></p>
<p>在运行ant时，如果指定了测试列表，就只运行指定的测试，否则运行所有的测试，Ant命令为：ant –D<span style="color: #000000;">scenarioListFile=file_name test，</span>相应的Ant配置如下：</p>
<pre class="brush: xml; toolbar: false">&lt;target name=&quot;execute-scenarios&quot; description=&quot;Executes scenarios&quot;&gt;
        &lt;path id=&quot;scenarios.classpath&quot;&gt;
            &lt;path refid=&quot;twist.libs&quot; /&gt;
            &lt;path refid=&quot;user.libs&quot; /&gt;
            &lt;path refid=&quot;fixtures.classes&quot; /&gt;
        &lt;/path&gt;

        &lt;taskdef classname=&quot;com.thoughtworks.twist.core.execution.ant.ExecuteScenariosTask&quot;
                 classpathref=&quot;scenarios.classpath&quot; name=&quot;twist.runner&quot; description=&quot;the Twist ant task&quot; /&gt;
        &lt;if&gt;
            &lt;isset property=&quot;scenarioListFile&quot;/&gt;
            &lt;then&gt;
                &lt;fileset dir=&quot;${twist.project.dir}/scenarios&quot; includesfile=&quot;${scenarioListFile}&quot; id=&quot;include.scenarios&quot;/&gt;
            &lt;/then&gt;
            &lt;else&gt;
                &lt;fileset dir=&quot;${twist.project.dir}/scenarios&quot; includes=&quot;**/*.scn&quot; id=&quot;include.scenarios&quot;/&gt;
            &lt;/else&gt;
        &lt;/if&gt;

        &lt;twist.runner scenarioDir=&quot;${twist.project.dir}/scenarios&quot; reportsDir=&quot;${twist.reports.output.dir}&quot; confDir=&quot;${twist.config.dir}&quot;
                       failureproperty=&quot;twist.scenarios.failed&quot; classpathref=&quot;scenarios.classpath&quot; tags=&quot;${twisttags}&quot; threads=&quot;1&quot;&gt;
            &lt;fileset refid=&quot;include.scenarios&quot;/&gt;
        &lt;/twist.runner&gt;            

        &lt;fail if=&quot;twist.scenarios.failed&quot; message=&quot;One or more scenarios for failed&quot; /&gt;
    &lt;/target&gt;</pre>
<p>更多信息请参考<a title="http://www.thoughtworks-studios.com/docs/twist/current/help/how_do_i_run_scenarios_from_ant.html" href="http://www.thoughtworks-studios.com/docs/twist/current/help/how_do_i_run_scenarios_from_ant.html">Twist的官方帮助文档(如何配置Ant, Maven, Rake)</a></p>
<h2>在Jenkins 上配置Twist 自动分配测试</h2>
<p>最后让我们把测试分成3份，并在Jenkins上把整个过程配置完成</p>
<p>最终的job关系图如下：</p>
<p><a href="http://www.theantway.com/wp-content/uploads/2012/04/job_steps1.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border-width: 0px;" title="job_steps" src="http://www.theantway.com/wp-content/uploads/2012/04/job_steps_thumb1.png" alt="job_steps" width="350" height="232" border="0" /></a></p>
<p>配置job的具体步骤是：</p>
<ol>
<li>创建Compile Job(提交代码后的跑的第一个Job, 进行编译和单元测试), 配置这个Job进行测试的分配<a href="http://www.theantway.com/wp-content/uploads/2012/04/compile_job_assign_tests1.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border-width: 0px;" title="compile_job_assign_tests" src="http://www.theantway.com/wp-content/uploads/2012/04/compile_job_assign_tests_thumb1.png" alt="compile_job_assign_tests" width="640" height="371" border="0" /></a>如图增加一个Build Step, 执行Shell script:
<pre class="brush: bash; toolbar: false">curl -z ~/test_distributor.py -L https://raw.github.com/theantway/test-distributor/master/test_distributor.py -o ~/test_distributor.py
curl -z ~/twist-tests-explorer-1.0.tar.gz -L https://github.com/downloads/theantway/test-distributor/twist-tests-explorer-1.0.tar.gz -o ~/twist-tests-explorer-1.0.tar.gz

mkdir -p test-distributor &amp;&amp; tar -xzf ~/twist-tests-explorer-1.0.tar.gz -C test-distributor
java -jar test-distributor/twist-tests-explorer-1.0.jar -s FunctionalTests/scenarios -t \\&#039;TEAM-FR &amp; !State-In-Progress\\&#039; | python ~/test_distributor.py --source-folder=previous-test-reports --blocks=5 --delimiter=\\&#039;\n\\&#039;</pre>
<p>指定Archive artifacts 包含生成的文件： test-blocks/*.txt</li>
<li>创建Multi-configuration 类型的Functional-Tests Job, 运行分配的部分测试，生成并保存Junit test 兼容的测试报告增加一个User Defined Axis, Name: testblock, Values: 1 2 3增加一个Build Step, 选择Copy Artifact(需要单独的插件支持)，选择从Compile Job复制Artifacts，这样我们就得到了分配好的测试列表执行测试：ant –D<span style="color: #000000;">scenarioListFile=tests-$testblock.txt test。然后再</span>指定Archive artifacts 包含生成的测试报告： test-reports/**/*.xml<a href="http://www.theantway.com/wp-content/uploads/2012/04/multi-configuration_job_run_tests.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border-width: 0px;" title="multi-configuration_job_run_tests" src="http://www.theantway.com/wp-content/uploads/2012/04/multi-configuration_job_run_tests_thumb.png" alt="multi-configuration_job_run_tests" width="800" height="887" border="0" /></a></li>
<li>修改Compile Job, 从Functional Tests 中获取Test Report, 从而不断根据最新的测试时间对测试分配进行调整增加一个Build Step, 选择Copy Artifact，选择从Functional-Tests Job复制Artifacts，这样我们就得到了历史测试记录。我们需要选上Optional 选项，因为第一次运行时还没有Artifacts<a href="http://www.theantway.com/wp-content/uploads/2012/04/compile_copy_artifact_from_function_tests1.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border: 0px;" title="compile_copy_artifact_from_function_tests" src="http://www.theantway.com/wp-content/uploads/2012/04/compile_copy_artifact_from_function_tests_thumb1.png" alt="compile_copy_artifact_from_function_tests" width="640" height="414" border="0" /></a></li>
</ol>
<h2>Jenkins上的测试结果页面效果</h2>
<p><a href="http://www.theantway.com/wp-content/uploads/2012/04/aggregated_test_result_of_functional_tests.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border-width: 0px;" title="aggregated_test_result_of_functional_tests" src="http://www.theantway.com/wp-content/uploads/2012/04/aggregated_test_result_of_functional_tests_thumb.png" alt="aggregated_test_result_of_functional_tests" width="640" height="292" border="0" /></a></p>
<h2>参考：</h2>
<ul>
<li>关于Jenkins上如何配置Multi-configuration Job的介绍：<a href="http://www.theantway.com/2012/04/09/using-multi-configuration-project-for-distributed-builds-on-jenkins/" target="_blank">Using multi-configuration project for distributed builds on Jenkins</a></li>
<li>本文代码库：<a title="https://github.com/theantway/test-distributor" href="https://github.com/theantway/test-distributor">https://github.com/theantway/test-distributor</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.theantway.com/2012/04/13/assign_tests_on_jenkins_based_on_build_history-cn/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using multi-configuration project for distributed builds on Jenkins</title>
		<link>http://www.theantway.com/2012/04/09/using-multi-configuration-project-for-distributed-builds-on-jenkins/</link>
		<comments>http://www.theantway.com/2012/04/09/using-multi-configuration-project-for-distributed-builds-on-jenkins/#comments</comments>
		<pubDate>Mon, 09 Apr 2012 06:05:05 +0000</pubDate>
		<dc:creator>xu wei</dc:creator>
				<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[Jenkins]]></category>
		<category><![CDATA[Multi-configuration Job]]></category>

		<guid isPermaLink="false">http://www.theantway.com/?p=124</guid>
		<description><![CDATA[Sometimes we need to run tests on different machines because of kinds of reasons: run tests on different environment, e.g. run tests against different OS, DB distributed test in order to get fast feedback, e.g. split tests into n parts, and only run one part on one machine &#8230; The following is a real screenshot &#8230; </p><p><a class="more-link block-button" href="http://www.theantway.com/2012/04/09/using-multi-configuration-project-for-distributed-builds-on-jenkins/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p>Sometimes we need to run tests on different machines because of kinds of reasons:</p>
<ol>
<li>run tests on different environment, e.g. run tests against different OS, DB </li>
<li>distributed test in order to get fast feedback, e.g. split tests into n parts, and only run one part on one machine </li>
<li>&#8230; </li>
</ol>
<div>
<p>The following is a real screenshot of Jenkins in one of my projects, we created several jobs for the same functional tests, and the only difference between them is what test tag(s) need to run on this job.</p>
<p><a href="http://www.theantway.com/wp-content/uploads/2012/04/list_of_duplicated_test_projects-e1333949356509.png"><img class="aligncenter" title="list of duplicated test projects" alt="" src="http://www.theantway.com/wp-content/uploads/2012/04/list_of_duplicated_test_projects-e1333949356509.png" width="705" height="288" /></a></p>
</p></div>
<h2>The Problem</h2>
<p>The settings for each job are almost same:</p>
<ul>
<li>Limit the build agent to run the tests using job label </li>
<li>Clean up test environment before test </li>
<li>Copy zipped artifact from upstream </li>
<li>Extract zip file </li>
<li>Run tests specified by tag(s)</li>
<li>Publish html reports </li>
<li>Publish xml reports </li>
</ul>
<p>The problem is any time the process need to be changed, we need to change all of them which is simple a copy paste work but need to repeat 7 times.</p>
<h2>The Solution</h2>
<p>Jenkins provides a Multi-configuration project support when create a new job, The only difference is to add an user-defined axis, and we can also add a Slave axis in order to limit the tests only runs on the desired agents. </p>
<p>Click the question icon if you don&#8217;t understand any setting because Jenkins provided a very helpful document.</p>
<p><a href="http://www.theantway.com/wp-content/uploads/2012/04/configure_page_of_multi_configuration_job.png"><img class="size-full wp-image-130 aligncenter" title="configure page of multi configuration job" alt="" src="http://www.theantway.com/wp-content/uploads/2012/04/configure_page_of_multi_configuration_job.png" width="687" height="541" /></a></p>
<p>The Multi-configuration job just likes a job template: it will start several sub jobs based on the configuration, aggregate all the test results and show in the status page:</p>
<p><a href="http://www.theantway.com/wp-content/uploads/2012/04/multi_configuration_jenkins_job_status_page.png"><img class="aligncenter" title="multi configuration jenkins job status page" alt="" src="http://www.theantway.com/wp-content/uploads/2012/04/multi_configuration_jenkins_job_status_page.png" width="541" height="242" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.theantway.com/2012/04/09/using-multi-configuration-project-for-distributed-builds-on-jenkins/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Could not capture screenshot when test failed on windows</title>
		<link>http://www.theantway.com/2012/04/06/could-not-capture-screenshot-when-test-failed-on-windows/</link>
		<comments>http://www.theantway.com/2012/04/06/could-not-capture-screenshot-when-test-failed-on-windows/#comments</comments>
		<pubDate>Fri, 06 Apr 2012 05:31:04 +0000</pubDate>
		<dc:creator>xu wei</dc:creator>
				<category><![CDATA[Functional Test]]></category>
		<category><![CDATA[Windows's Tips]]></category>
		<category><![CDATA[blank image]]></category>
		<category><![CDATA[Screenshot]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://www.theantway.com/?p=108</guid>
		<description><![CDATA[Problems: We have configured to capture screenshot when selenium (or any web based automation test, e.g. web driver, sahi, etc&#8230; ) tests failed, but each time we got is a blank image which only has a black background. Environment: Windows 2008 on a virtual machine Reason: There is no a real window opened because nobody logged &#8230; </p><p><a class="more-link block-button" href="http://www.theantway.com/2012/04/06/could-not-capture-screenshot-when-test-failed-on-windows/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<h2>Problems:</h2>
<p>We have configured to capture screenshot when selenium (or any web based automation test, e.g. web driver, sahi, etc&#8230; ) tests failed, but each time we got is a blank image which only has a black background.</p>
<p><a href="http://www.theantway.com/wp-content/uploads/2012/04/blank_image.png"><img class="alignnone size-thumbnail wp-image-113" title="blank_black_ground_image" src="http://www.theantway.com/wp-content/uploads/2012/04/blank_image-150x150.png" alt="blank black ground image" width="150" height="150" /></a></p>
<h2>Environment:</h2>
<p>Windows 2008 on a virtual machine</p>
<h2>Reason:</h2>
<p>There is no a real window opened because nobody logged on this machine, so the captured image is only a blank image. <strong>The solution is turn on auto logon for windows and never lock the desktop</strong></p>
<h2>Solution:</h2>
<h3>turn on auto logon for windows</h3>
<p style="padding-left: 30px;">References:<br />
<a href="http://support.microsoft.com/kb/324737">http://support.microsoft.com/kb/324737<br />
</a><a href="http://answers.microsoft.com/en-us/windows/forum/windows_7-security/how-to-turn-on-automatic-logon-in-windows-7/99d4fe75-3f22-499b-85fc-c7a2c4f728af">http://answers.microsoft.com/en-us/windows/forum/windows_7-security/how-to-turn-on-automatic-logon-in-windows-7/99d4fe75-3f22-499b-85fc-c7a2c4f728af</a></p>
<p style="padding-left: 30px;">Based on the above posts, the manual steps are:<br />
run command: <em>control userpasswords2<br />
</em>Select <strong>the account</strong> from the list, uncheck the <em>&#8220;Users must enter a user name and password to use this computer. &#8221; and apply</em></p>
<p style="padding-left: 30px;">You can also edit the registry, and the steps can be applied to multiple machines using psexec in this way, but I didn&#8217;t try that.</p>
<h3>never lock the desktop</h3>
<p style="padding-left: 30px;">References:<br />
<a href="http://social.technet.microsoft.com/Forums/en-US/winservergen/thread/520a8128-7e9d-4983-89d2-f414a6ec8188/">http://social.technet.microsoft.com/Forums/en-US/winservergen/thread/520a8128-7e9d-4983-89d2-f414a6ec8188/</a></p>
<p style="padding-left: 30px;">The manual steps are:</p>
<ol style="padding-left: 30px;">
<li>Right click on desktop, select Personalization</li>
<li>Select Screen Saver,</li>
<li>Clear the check box &#8220;On resume, display logon screen&#8221;.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.theantway.com/2012/04/06/could-not-capture-screenshot-when-test-failed-on-windows/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Google Search 技巧之 矫正发音</title>
		<link>http://www.theantway.com/2011/11/09/google-search-%e6%8a%80%e5%b7%a7%e4%b9%8b-%e7%9f%ab%e6%ad%a3%e5%8f%91%e9%9f%b3/</link>
		<comments>http://www.theantway.com/2011/11/09/google-search-%e6%8a%80%e5%b7%a7%e4%b9%8b-%e7%9f%ab%e6%ad%a3%e5%8f%91%e9%9f%b3/#comments</comments>
		<pubDate>Wed, 09 Nov 2011 05:45:26 +0000</pubDate>
		<dc:creator>xu wei</dc:creator>
				<category><![CDATA[google search]]></category>
		<category><![CDATA[accent]]></category>

		<guid isPermaLink="false">http://www.theantway.com/blog/?p=78</guid>
		<description><![CDATA[自己知道英语水平不高，但一直也没有人认真的说我的发音有问题。直到有一天，到了美国，发现经常需要把单词拼出来，别人才知道什么意思，于是忽然意识到了问题的严重性。 第一反应就是google一下语音矫正，发现了一篇帖子： http://www.ewedu.net/f/t/10131/35788.html 更加震撼，于是想着找一些软件来提高口语，但没有找到合适的， 突然想起（发现）google search是支持语音的，马上试验了一下，找出了发音比较简单的单词，试了一下效果，识别正确。 于是开始实验别人听不懂的单词，发现google也听不懂 ：-） 接下来就把google不能识别的单词记下来，查词典看音标，不断尝试，感觉改进了不少。 两天后，再向别人说起以前发音有问题的单词，已经几乎没有问题了。 注： 1. 在google Search 输入框的右侧，有个麦克风的图标，点击就可以语音输入了。 2. 有网站介绍说，不能迷信语音输入，个人感觉至少可以帮助我们一定程度上发现问题 3. 另一个参考网站：http://www.accentschool.com]]></description>
			<content:encoded><![CDATA[<p>自己知道英语水平不高，但一直也没有人认真的说我的发音有问题。直到有一天，到了美国，发现经常需要把单词拼出来，别人才知道什么意思，于是忽然意识到了问题的严重性。</p>
<p>第一反应就是google一下语音矫正，发现了一篇帖子： <a href="http://www.ewedu.net/f/t/10131/35788.html">http://www.ewedu.net/f/t/10131/35788.html</a></p>
<p>更加震撼，于是想着找一些软件来提高口语，但没有找到合适的， 突然想起（发现）google search是支持语音的，马上试验了一下，找出了发音比较简单的单词，试了一下效果，识别正确。 于是开始实验别人听不懂的单词，发现google也听不懂 ：-）</p>
<p>接下来就把google不能识别的单词记下来，查词典看音标，不断尝试，感觉改进了不少。</p>
<p>两天后，再向别人说起以前发音有问题的单词，已经几乎没有问题了。</p>
<p>注：</p>
<p>1. 在google Search 输入框的右侧，有个麦克风的图标，点击就可以语音输入了。</p>
<p>2. 有网站介绍说，不能迷信语音输入，个人感觉至少可以帮助我们一定程度上发现问题</p>
<p>3. 另一个参考网站：<a href="http://www.accentschool.com">http://www.accentschool.com</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.theantway.com/2011/11/09/google-search-%e6%8a%80%e5%b7%a7%e4%b9%8b-%e7%9f%ab%e6%ad%a3%e5%8f%91%e9%9f%b3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Could not compile libxml2: /bin/rm: cannot remove `libtoolT&#8217;: No such file or directory</title>
		<link>http://www.theantway.com/2011/06/02/could-not-compile-libxml2-binrm-cannot-remove-libtoolt-no-such-file-or-directory-2/</link>
		<comments>http://www.theantway.com/2011/06/02/could-not-compile-libxml2-binrm-cannot-remove-libtoolt-no-such-file-or-directory-2/#comments</comments>
		<pubDate>Thu, 02 Jun 2011 02:15:00 +0000</pubDate>
		<dc:creator>xu wei</dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://www.theantway.com/blog/2011/06/02/could-not-compile-libxml2-binrm-cannot-remove-libtoolt-no-such-file-or-directory-2/</guid>
		<description><![CDATA[When I compile libxml2-2.7.8 under Ubuntu 11.04, it always complain that: /bin/rm: cannot remove `libtoolT&#8217;: No such file or directory. I did a search on google, but none of them works. From the console output, I found that it failed because removing a file which does not exists. So I edit the configure file, find the &#8230; </p><p><a class="more-link block-button" href="http://www.theantway.com/2011/06/02/could-not-compile-libxml2-binrm-cannot-remove-libtoolt-no-such-file-or-directory-2/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p>When I compile libxml2-2.7.8 under Ubuntu 11.04, it always complain that:</p>
<blockquote><p>/bin/rm: cannot remove `libtoolT&#8217;: No such file or directory.</p></blockquote>
<p>I did a search on google, but none of them works.</p>
<p>From the console output, I found that it failed because removing a file which does not exists.</p>
<p>So I edit the configure file, find the line which contains $RM &#8220;$cfgfile&#8221;, and replace it with $RM -f &#8220;$cfgfile&#8221;, then everything works.</p>
<p>(&#8216;rm -f&#8217; means do not stop even if the file doesn&#8217;t exists)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.theantway.com/2011/06/02/could-not-compile-libxml2-binrm-cannot-remove-libtoolt-no-such-file-or-directory-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Connection always successful when installed anti-virus application</title>
		<link>http://www.theantway.com/2009/07/15/connection-always-successful-when-installed-anti-virus-application-2/</link>
		<comments>http://www.theantway.com/2009/07/15/connection-always-successful-when-installed-anti-virus-application-2/#comments</comments>
		<pubDate>Wed, 15 Jul 2009 05:30:00 +0000</pubDate>
		<dc:creator>xu wei</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.theantway.com/blog/2009/07/15/connection-always-successful-when-installed-anti-virus-application-2/</guid>
		<description><![CDATA[When I try to use URLConnection to check if a url is accessible using the following code: try { URL url = new URL("http://169.254.169.254/latest"); URLConnection connection = url.openConnection(); connection.setConnectTimeout(5000); connection.connect(); System.out.println("Connected successfully using url"); } catch (IOException e) { e.printStackTrace(); } I expected the behavior is: connect should be success if the host is reachable, &#8230; </p><p><a class="more-link block-button" href="http://www.theantway.com/2009/07/15/connection-always-successful-when-installed-anti-virus-application-2/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p>When I try to use URLConnection to check if a url is accessible using the following code:</p>
<pre class="brush: java;">try {
    URL url = new URL("http://169.254.169.254/latest");

    URLConnection connection = url.openConnection();
    connection.setConnectTimeout(5000);
    connection.connect();
    System.out.println("Connected successfully using url");
} catch (IOException e) {
        e.printStackTrace();
}</pre>
<p>I expected the behavior is: connect should be success if the host is reachable, else throw exception. It works fine without the anti-virus application, but always print “connected successfully” even the host is not reachable. Then I tried to use Socket to connect:</p>
<pre class="brush: java;">Socket socket = new Socket();
socket.connect(new InetSocketAddress("169.254.169.254", 80));
if (socket.isConnected()) {
    System.out.println("Connected successfully using socket");
} else {
    System.out.println("Connected failed using socket");
}</pre>
<p>But Still got the same problem. The solution for it: Disable http check in anti-virus, for example, in ESET NOD32, the settings is Web access protection -&gt; Http, Https -&gt; Http scanner <a href="http://www.theantway.com/wp-content/uploads/2011/11/ESET_http_connection_always_success_2.png"><img class="alignnone size-full wp-image-55" title="ESET_http_connection_always_success" src="http://www.theantway.com/wp-content/uploads/2011/11/ESET_http_connection_always_success_2.png" alt="http connection always success" width="640" height="407" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.theantway.com/2009/07/15/connection-always-successful-when-installed-anti-virus-application-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Adding text to an image using Preview on Mac 10.5 (Most difficult to use feature I have met in ten years)</title>
		<link>http://www.theantway.com/2009/07/14/adding-text-to-an-image-using-preview-on-macmost-difficult-to-use-feature-i-have-met-in-ten-years-2/</link>
		<comments>http://www.theantway.com/2009/07/14/adding-text-to-an-image-using-preview-on-macmost-difficult-to-use-feature-i-have-met-in-ten-years-2/#comments</comments>
		<pubDate>Tue, 14 Jul 2009 02:49:00 +0000</pubDate>
		<dc:creator>xu wei</dc:creator>
				<category><![CDATA[Mac]]></category>

		<guid isPermaLink="false">http://www.theantway.com/blog/2009/07/14/adding-text-to-an-image-using-preview-on-macmost-difficult-to-use-feature-i-have-met-in-ten-years-2/</guid>
		<description><![CDATA[Preview support edit images, but the menu item is disabled by default when I open an image file. I thought it was because the Preview does not support this kind of file type. Recently, when I try to find a simple image editor only for add some text, oval or rectangle, I found that the &#8230; </p><p><a class="more-link block-button" href="http://www.theantway.com/2009/07/14/adding-text-to-an-image-using-preview-on-macmost-difficult-to-use-feature-i-have-met-in-ten-years-2/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p>Preview support edit images, but the menu item is disabled by default when I open an image file. I thought it was because the Preview does not support this kind of file type. Recently, when I try to find a simple image editor only for add some text, oval or rectangle, I found that the Preview support it perfectly.</p>
<p>After customized the toolbar, the buttons enabled, and it’s very easy to add comments, but the menu item still disabled.</p>
<p>The following is what I found about how to customize the toolbar from the Preview Help.</p>
<p><strong>Adding text to an image</strong></p>
<p style="padding-left: 30px;">You can add text to an image to describe what’s in it or when the image was created.</p>
<p style="padding-left: 30px;">After you save the image, you can’t edit, move, or delete any text you added to it. If you think you’ll need to edit the text, convert the image to a PDF document, and then add notes to the PDF document. Notes added to a PDF document can be edited after they’re saved.</p>
<p style="padding-left: 30px;">To add text to an image:</p>
<ol style="padding-left: 30px;">
<li>If the Annotate pop-up menu isn’t in the toolbar, choose View &gt; Customize Toolbar and drag the Annotate pop-up menu to the toolbar.</li>
<li>Choose Note from the Annotate pop-up menu in the toolbar.</li>
<li>Drag over the area where you want the text to appear.</li>
<li>Enter your text.</li>
</ol>
<p style="padding-left: 30px;">Before you save the image, you can move, resize, edit, or delete the text. First choose Text Annotation from the Annotation pop-up menu in the toolbar. Then to edit the text, double-click it. To delete the text, click it so resize handles appear, and then press Delete.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.theantway.com/2009/07/14/adding-text-to-an-image-using-preview-on-macmost-difficult-to-use-feature-i-have-met-in-ten-years-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>基于JavaScript的代码自动生成工具</title>
		<link>http://www.theantway.com/2008/03/05/%e5%9f%ba%e4%ba%8ejavascript%e7%9a%84%e4%bb%a3%e7%a0%81%e8%87%aa%e5%8a%a8%e7%94%9f%e6%88%90%e5%b7%a5%e5%85%b7-2/</link>
		<comments>http://www.theantway.com/2008/03/05/%e5%9f%ba%e4%ba%8ejavascript%e7%9a%84%e4%bb%a3%e7%a0%81%e8%87%aa%e5%8a%a8%e7%94%9f%e6%88%90%e5%b7%a5%e5%85%b7-2/#comments</comments>
		<pubDate>Wed, 05 Mar 2008 05:28:00 +0000</pubDate>
		<dc:creator>xu wei</dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.theantway.com/blog/2008/03/05/%e5%9f%ba%e4%ba%8ejavascript%e7%9a%84%e4%bb%a3%e7%a0%81%e8%87%aa%e5%8a%a8%e7%94%9f%e6%88%90%e5%b7%a5%e5%85%b7-2/</guid>
		<description><![CDATA[JavaScript Based Code Generator &#8211; codegen 工具主页 http://thelei.sourceforge.net 目的 快速生成程序代码, 比如Struts, Spring, Jdbc/Hibernate所有前后台的代码. 简单介绍 本工具生成代码的思想是读取数据库中表的结构, 使用JavaScript作为脚本语言编写模板, 生成各种代码或者文件, 支持各种格式的文本文件, Java, C#, PHP 等, 只要是文本文件, 都可以生成, 因为生成什么是由你完全自己定义的. 对数据库的访问是通过插件的形式进行的, 所以易于扩充, 目前支持Mysql, 以及支持Ado连接的数据库. 本工具开发于2006年, 经过两年的内部使用, 进行了不断改进, 现在把它公布出来, 希望能有更多的人用它, 提意见, 然后把它做的更好. 什么时候用它 有时候在项目开发过程中有大量的简单重复劳动, 以Struts, Spring, Jdbc/Hibernate为例, 对数据库中的每张表, 都有其相对应的Dao(2), Service(2), Action(Class+Validation2), Jsp(list+edit+add=3), 需要新建10多个文件/目录,  而这个过程是很枯燥的, 但又会花不少时间, 这个工具的最初开发目的就是自动生成这些文件. 这样,新增一个列表,增删改的模块, 如果不考虑界面需要调整, 就只需要几分钟的时间, 换句话说, 你可以在很短的时间内把所有需要的表自动生成列表,增删改的功能. &#8230; </p><p><a class="more-link block-button" href="http://www.theantway.com/2008/03/05/%e5%9f%ba%e4%ba%8ejavascript%e7%9a%84%e4%bb%a3%e7%a0%81%e8%87%aa%e5%8a%a8%e7%94%9f%e6%88%90%e5%b7%a5%e5%85%b7-2/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<h2>JavaScript Based Code Generator &#8211; codegen<br />
<strong></strong></h2>
<h3>工具主页</h3>
<p><a href="http://thelei.sourceforge.net">http://thelei.sourceforge.net</a></p>
<h3>目的</h3>
<p>快速生成程序代码, 比如Struts, Spring, Jdbc/Hibernate所有前后台的代码.</p>
<h3>简单介绍</h3>
<p>本工具生成代码的思想是读取数据库中表的结构, 使用JavaScript作为脚本语言编写模板, 生成各种代码或者文件, 支持各种格式的文本文件, Java, C#, PHP 等, 只要是文本文件, 都可以生成, 因为生成什么是由你完全自己定义的. 对数据库的访问是通过插件的形式进行的, 所以易于扩充, 目前支持Mysql, 以及支持Ado连接的数据库.</p>
<p>本工具开发于2006年, 经过两年的内部使用, 进行了不断改进, 现在把它公布出来, 希望能有更多的人用它, 提意见, 然后把它做的更好.</p>
<h3>什么时候用它</h3>
<p>有时候在项目开发过程中有大量的简单重复劳动, 以Struts, Spring, Jdbc/Hibernate为例, 对数据库中的每张表, 都有其相对应的Dao(2), Service(2), Action(Class+Validation2), Jsp(list+edit+add=3), 需要新建10多个文件/目录,  而这个过程是很枯燥的, 但又会花不少时间, 这个工具的最初开发目的就是自动生成这些文件. 这样,新增一个列表,增删改的模块, 如果不考虑界面需要调整, 就只需要几分钟的时间, 换句话说, 你可以在很短的时间内把所有需要的表自动生成列表,增删改的功能.</p>
<h3>为什么用它</h3>
<p>答案很简单, 因为这个程序简单容易使用, 而且完全自定义, 能满足不同项目对代码的要求, 只要你能描述出要生成的代码结构和形式的共性,就能把模板写出来.</p>
<h3>简单的例子</h3>
<pre class="brush: java; gutter: true">public class Base&lt;%=getDomainByTable(sys_table_name)%&gt;{
&lt;$
for(var i=0;i&lt;sys_fields.length;i++){
    var type=getJavaBeanType(sys_fields[i].type);
$&gt;
    private&lt;%=type%&gt;&lt;%=sys_fields[i].name%&gt;;
&lt;$
}

for(var i=0;i&lt;sys_fields.length;i++){
    var type=getJavaBeanType(sys_fields[i].type);
$&gt;
    public&lt;%=type%&gt;get&lt;%=formatFieldName(sys_fields[i].name)%&gt;(){
        return&lt;%=sys_fields[i].name%&gt;;
    }

    public void set&lt;%=formatFieldName(sys_fields[i].name)%&gt;(&lt;%=type%&gt;&lt;%=sys_fields[i].name%&gt;){
        this.&lt;%=sys_fields[i].name%&gt;=&lt;%=sys_fields[i].name%&gt;;
    }

&lt;$
}
$&gt;
}</pre>
<p>如果当前的表名是:Role, 同时它有role_id int, role_name varchar, description varchar等字段, 那么生成的结果就是:</p>
<pre class="brush: java; gutter: true">public class BaseRole {
    private int role_id;
    private String role_name;
    private String description;

    public int getRoleId() {
        return role_id;
    }

    public void setRoleId(int role_id) {
        this.role_id = role_id;
    }

    public String getRoleName() {
        return role_name;
    }

    public void setRoleName(String role_name) {
        this.role_name = role_name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}</pre>
<p>从模板里可以看到, 要生成什么样的代码都是自己可以定制的, 包括方法的起名, 例子里formatFieldName是自己写的JavaScript 方法.</p>
<h3>脚本块</h3>
<ol>
<li>&lt;$<br />
//Javascript code like: println(sys_fields[0].name);<br />
$&gt; 执行一段代码</li>
<li>&lt;$=sys_fields[0].name$&gt; 显示表达式的值</li>
</ol>
<h3>预定义的变量</h3>
<ol>
<li>sys_table_name: 当前表名</li>
<li>sys_fields: 当前表的Field 对象数组. 使用sys_fields.length 来获取字段的数目.</li>
<li>sys_keys: 当前表主键的数组, 使用sys_keys.length来获取主键字段的数量.</li>
<li>sys_user_name: 当前连接数据库的用户名</li>
<li>sys_db_name: 当前连接到数据库的名字</li>
<li>sys_output: 内部使用,存储生成的中间JavaScript代码</li>
<li>Field 对象属性, 如果有一个字段: user_name varchar(100) not null default &#8216;user&#8217;, 下面是Field的属性和值:<br />
name: 字段名, String. 如: sys_fields[i].name, 例子值是 user_name<br />
type: 字段类型, String, 如: sys_fields[i].type, 例子值是 varchar<br />
size: 字段长度, integer, 如: sys_fields[i].size, 例子值是 100<br />
scale: 精度, integer, 如: sys_fields[i].scale, 本例子是varchar 类型,本项为空<br />
default_value: 默认值, String. 如: sys_fields[i].default_value, 例子值是 user<br />
is_null: 是否可以为空, boolean. 如: sys_fields[i].is_null, 例子值是 false<br />
is_primary_key: 是否是主键, boolean. 如: sys_fields[i].is_primary_key, 例子值是 false.</li>
</ol>
<h3>预定义的方法</h3>
<ol>
<li>print(str). 输出str, 没有换行</li>
<li>println(str). 输出str, 结尾换行</li>
<li>getFieldList(). 返回字段列表, 用&#8217;,&#8217; 分割</li>
<li>一些其他的javascript 方法, 如capitalize, lowercase, uppercase, trim等等. 你也可以通过Tools/Edit public functions增加自己常用的方法</li>
</ol>
<h3>使用步骤</h3>
<ol>
<li>创建一个新项目, 设定数据库连接参数</li>
<li>添加目录和模板, 模板采用JavaScript脚本语言, 唯一的区别是用&lt;$ you code$&gt; 把代码包起来, 或者用&lt;%=expression%&gt; 来打印一个表达式</li>
<li>生成文件, 分为三种方式: 生成文件, 生成项目中的所有文件, 批量生成(多表, 多模板)</li>
</ol>
<h3>数据库连接参数</h3>
<ol>
<li>选择数据库类型</li>
<li>输入需要的信息, 如host, port, username, password and database name</li>
</ol>
<h3>生成文件</h3>
<ol>
<li>生成当前文件, Template/Generate current file</li>
<li>生成项目的所有文件, Project/Generate Project Files</li>
<li>批量生成, Project/Batch Generate, 然后选择需要生成文件的tables和 templates(folders) 生成文件.</li>
</ol>
<h3>Tips</h3>
<ol>
<li>使用JavaScript map, 而不是if &#8230; else &#8230;, 这样代码看着非常简洁
<pre class="brush: javascript; gutter: true">&lt;$
    var type_defaultvalue={
    'int':'0',
    'tinyint':'false',
    'varchar':'""',
    'datetime':'new Date()',
    };

    for (var i=0; i&lt;sys_fields.length; i++){
        var type = sys_fields[i].type;

        if(undef(type_defaultvalue[type])){
            println("Undefined default value for field type: '" + type + "'");
        }

        //the following line has the same result
        //println(sys_table_name + ".set" + formatFieldName(sys_fields[i].name) + "(" + type_defaultvalue[type] + ");");
$&gt;
        &lt;%=sys_table_name%&gt;.set&lt;%=formatFieldName(sys_fields[i].name)%&gt;(&lt;%=type_defaultvalue[type] %&gt;);
&lt;$
    }
$&gt;</pre>
</li>
<li>在项目属性对话框中写一些项目使用的JavaScript方法, 然后可以为不同的表生成不同的模块, 如:
<pre class="brush: javascript; gutter: true">function getModule(tablename){
    if(tablename.startsWith('user')){
        return "user/";
    }

    //code to return other module name
    //...
    return "";
}</pre>
<p>然后设置目录或模板的destination path为: &lt;%=getModule(sys_table_name)%&gt;&lt;%=getDomainByTable(sys_table_name)%&gt;Action.java, 如果当前的表是user, 目标路径将是: user/UserAction.java, 如果表名是 log, 目标路径将是: LogAction.java</li>
</ol>
<h3>改进这个工具:</h3>
<p>如果有下面的问题, 请和我联系:</p>
<ol>
<li>需要一个新的数据库插件或者你写了一个新的数据库插件</li>
<li>不知道如何使用这个工具</li>
<li>发现了Bug</li>
<li>其他任意的建议&#8230;</li>
</ol>
<p>&nbsp;</p>
<p>最后希望这个工具对你能够有所帮助, 同时减少编码时Copy&amp;Paste的时间</p>
]]></content:encoded>
			<wfw:commentRss>http://www.theantway.com/2008/03/05/%e5%9f%ba%e4%ba%8ejavascript%e7%9a%84%e4%bb%a3%e7%a0%81%e8%87%aa%e5%8a%a8%e7%94%9f%e6%88%90%e5%b7%a5%e5%85%b7-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introduce a tool to generate code by writing JavaScript</title>
		<link>http://www.theantway.com/2008/03/05/introduce-a-tool-to-generate-code-by-writing-javascript-2/</link>
		<comments>http://www.theantway.com/2008/03/05/introduce-a-tool-to-generate-code-by-writing-javascript-2/#comments</comments>
		<pubDate>Tue, 04 Mar 2008 14:48:00 +0000</pubDate>
		<dc:creator>xu wei</dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://www.theantway.com/blog/2008/03/05/introduce-a-tool-to-generate-code-by-writing-javascript-2/</guid>
		<description><![CDATA[Java Based Code Generator &#8211; jbcgen home page: http://sourceforge.net/projects/jbcgen/ Short Description: This code generator generates files from database and templates written in JavaScript. it supports db plugin, and it&#8217;s easy to extend and use. It supports mysql and db which support ado connection(under windows only) for now. When To Use It should be used especially &#8230; </p><p><a class="more-link block-button" href="http://www.theantway.com/2008/03/05/introduce-a-tool-to-generate-code-by-writing-javascript-2/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<h2>Java Based Code Generator &#8211; jbcgen<br />
<strong></strong></h2>
<h3><strong>home page</strong>:</h3>
<p><a href="http://sourceforge.net/projects/jbcgen/">http://sourceforge.net/projects/jbcgen/</a></p>
<h3>Short Description:</h3>
<p>This code generator generates files from database and templates written in JavaScript. it supports db plugin, and it&#8217;s easy to extend and use. It supports mysql and db which support ado connection(under windows only) for now.</p>
<h3>When To Use</h3>
<p>It should be used especially when the code logic is similar, but the table is different. It will save much of your time by generating code automatically instead of copy&amp;paste. For example, you want to write 5 files for each table, you only need to write lines of javascript code without coding the files lots of time.</p>
<h3>Why To Use</h3>
<p>The reason is very simple, the program is very simple and it will save much of your time.</p>
<h3>A very simple example:</h3>
<pre class="brush: java; gutter: true">public class Base&lt;%=getDomainByTable(sys_table_name)%&gt;{
&lt;$
for(var i=0;i&lt;sys_fields.length;i++){
    var type=getJavaBeanType(sys_fields[i].type);
$&gt;
    private&lt;%=type%&gt;&lt;%=sys_fields[i].name%&gt;;
&lt;$
}

for(var i=0;i&lt;sys_fields.length;i++){
    var type=getJavaBeanType(sys_fields[i].type);
$&gt;
    public&lt;%=type%&gt;get&lt;%=formatFieldName(sys_fields[i].name)%&gt;(){
        return&lt;%=sys_fields[i].name%&gt;;
    }

    public void set&lt;%=formatFieldName(sys_fields[i].name)%&gt;(&lt;%=type%&gt;&lt;%=sys_fields[i].name%&gt;){
        this.&lt;%=sys_fields[i].name%&gt;=&lt;%=sys_fields[i].name%&gt;;
    }

&lt;$
}
$&gt;
}</pre>
<p>If the current table has role_id int, role_name varchar, description varchar, then the result will be:</p>
<pre class="brush: java; gutter: true">public class BaseRole {
    private int role_id;
    private String role_name;
    private String description;

    public int getRoleId() {
        return role_id;
    }

    public void setRoleId(int role_id) {
        this.role_id = role_id;
    }

    public String getRoleName() {
        return role_name;
    }

    public void setRoleName(String role_name) {
        this.role_name = role_name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}</pre>
<h3>Script Tag:</h3>
<ol>
<li>&lt;$<br />
//Javascript code like: println(sys_fields[0].name);<br />
$&gt;</li>
<li>&lt;$=sys_fields[0].name$&gt;</li>
</ol>
<h3>Predefined variables:</h3>
<ol>
<li>sys_table_name: current selected table name</li>
<li>sys_fields: Array of Field object. use sys_fields.length to get the field count of current table.</li>
<li>sys_keys: Array of Primary key column, use sys_keys.length to get the field count of current table.</li>
<li>sys_user_name: User name used to connect to current database.</li>
<li>sys_db_name: Database name used to connect to.</li>
<li>sys_output: Internal use only which store the temporary generated code. User can use it too by println(sys_output).</li>
<li>Field Object, if there&#8217;s a field: user_name varchar(100) not null default &#8216;user&#8217;, here&#8217;s the available fields and values:<br />
name: field name, String. For Example: sys_fields[i].name, example value is user_name<br />
type: field type, String, For Example: sys_fields[i].type, example value is varchar<br />
size: field length, integer, For Example: sys_fields[i].size, example value is 100<br />
scale: field scale, integer, For Example: sys_fields[i].scale, not available in this case<br />
default_value: Default value for the field, String. For Example: sys_fields[i].default_value, example value is user<br />
is_null: if the field can be null or not, boolean. For Example: sys_fields[i].is_null, example value is false<br />
is_primary_key: if the field is primary key, boolean. For Example: sys_fields[i].is_primary_key, example value is false.</li>
<li>sys_tables: all table&#8217;s name in current database.</li>
</ol>
<h3>Predefined Functions:</h3>
<ol>
<li>print(str). Print the code without new line.</li>
<li>println(str). Print the code with new line.</li>
<li>getFieldList(). Return field list separated by &#8216;,&#8217;</li>
<li>And a few other javascript functions, like capitalize, lowercase, uppercase, trim, etc. which you can add your own by Tools/Edit public functions</li>
</ol>
<h3>Steps to use this program:</h3>
<ol>
<li>Create a project</li>
<li>Add folder(s) and templates, template is written in JavaScript, and the only difference is it need the following format: &lt;$ you code$&gt; or &lt;%=expression%&gt;</li>
<li>Generate files for multiple tables and templates(folders)<br />
<h3></h3>
</li>
</ol>
<h3></h3>
<h3>DB Connection Setting</h3>
<p>Steps to config the database:</p>
<ol>
<li>Select Database Type. Click next.</li>
<li>Input required information like host, port, username, password and database name depends on your database type.</li>
</ol>
<h3>Generate Files</h3>
<p>Generate current file, Template/Generate current file</p>
<p>Generate project files, Project/Generate Project Files</p>
<p>Batch Generate, Project/Batch Generate, and then select tables and templates(folders) to generate files</p>
<h3>Tips</h3>
<ol>
<li>use JavaScript map instead of if &#8230; else &#8230;
<pre class="brush: javascript; gutter: true">&lt;$
    var type_defaultvalue={
    'int':'0',
    'tinyint':'false',
    'varchar':'""',
    'datetime':'new Date()',
    };

    for (var i=0; i&lt;sys_fields.length; i++){
        var type = sys_fields[i].type;

        if(undef(type_defaultvalue[type])){
            println("Undefined default value for field type: '" + type + "'");
        }

        //the following line has the same result
        //println(sys_table_name + ".set" + formatFieldName(sys_fields[i].name) + "(" + type_defaultvalue[type] + ");");
$&gt;
        &lt;%=sys_table_name%&gt;.set&lt;%=formatFieldName(sys_fields[i].name)%&gt;(&lt;%=type_defaultvalue[type] %&gt;);
&lt;$
    }
$&gt;</pre>
</li>
<li>write JavaScript function for project in project properties, and call them to put different tables in different place, e.g.
<pre class="brush: javascript; gutter: true">function getModule(tablename){
    if(tablename.startsWith('user')){
        return "user/";
    }

    //code to return other module name
    //...
    return "";
}</pre>
<p>and set the destination path for folder or template using this function: &lt;%=getModule(sys_table_name)%&gt;&lt;%=getDomainByTable(sys_table_name)%&gt;Action.java, then if the current table is user, the destination path will be: user/UserAction.java, and if the table is log, then the destination page will be: LogAction.java</li>
</ol>
<h3>Improve this program:</h3>
<p>Please write to me if:</p>
<ol>
<li>You need other type db connection or you wrote another db plugin</li>
<li>You don&#8217;t know how to use it</li>
<li>If you found any bug(s)</li>
<li>Any suggestion and anything else&#8230;</li>
</ol>
<p>&nbsp;</p>
<p>Hope it&#8217;s useful for you. And hope it will decrease your time of copy &amp; paste.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.theantway.com/2008/03/05/introduce-a-tool-to-generate-code-by-writing-javascript-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

