首页 文章 精选 留言 我的

精选列表

搜索[系统工具],共10000篇文章
优秀的个人博客,低调大师

Thunderbird 68.2.0 发布,邮件工具

Thunderbird 68.2.0已经发布了,该版本主要是一个 bug 修复版本。它引入了对两个 WebExtenance API、消息显示(Message Display)和消息搜索(Message Search)的支持。 Message Display:消息显示在 3-pane 选项卡、自己的选项卡或自己的窗口中 Message Search:获取具有指定属性的所有消息,或者如果没有指定属性,则获取所有消息 其他改进内容有: 当使用电子邮件客户端的暗主题时,对未读消息有更好的视觉反馈 修复编辑邮件列表时的各种问题 修复应用程序窗口在重新启动后不保持其大小 修复从 32 位版本升级到 64 位版本时的问题 下载地址: https://www.thunderbird.net/

优秀的个人博客,低调大师

Mock工具wiremock-py

作者 | 咪咪 wiremock-py wiremock-py 是基于WireMock实现的, 使用Python批量生成不同 测试场景 下不同HTTP API的 mock 数据, 然后作为mock server快速全面地对 API 进行测试。 背景 在数澜地产应用的前端测试中, 前端一般依赖于后端的数据, 前端通过后端在网关上发布的 HTTP API 获取数据. 要对前端进行充分的测试, 理想的做法是, 等待后端部署完成, 并且在数据层直接输入不同类型的数据源, 然后前端直接调用后端发布在网关上的 API 进行测试。 然而现实的情况是, 前端和后端的开发进度不完全一致, 如果前端先开发完成了, 必须要等后端对应的 API 开发完成后才能开始测试, 而且数据层的数据也不容易构造。 为了解决这个问题, 网关平台做了简单的 mock 功能, 每个 API 可以填写一个 mock数据, 然后前端调用 API 时直接使用这个 mock数据: 这种方式下, 网关充当了mock server: 但由于大家都使用同一个网关, 一个 API 只能保存一份 mock 数据, 所以有以下一些缺点: 不同的测试场景需要不同的 mock 数据来测试, 此时需要删掉上个测试场景的 mock 数据, 再创建新场景的 mock 数据才能进行测试 不能根据测试场景来按照一定的规则动态生成 API 对应的 mock 数据 不能多人同时使用测试同一个 API时, 只能都使用同一份 mock 数据, 不能各用各的 wiremock-py 可以解决上述这些问题: wiremock-py 通过传入不同的测试场景参数来生成不同的 mock 数据, 同时不同测试场景下使用的 mock 数据可以保存起来; 生成 mock 数据时, wiremock-py 支持使用Python和js代码来动态生成 mock 数据(也支持直接使用 json 数据, 如果 mock 数据中的数据量很大, 人工手写 mock 时的数据量会很大, 使用代码生成则比较容易); 不同的测试人员使用各自自己的 mock server, 不会影响到其他测试人员的测试。 测试人员需要做的是: 确定哪些 API 需要进行 mock 以及不同测试场景下对应的 mock 规则是什么。 依赖环境 Java 1.8.0_144 Node v8.6.0 Python 3.4.3 演示 快速开始 以贸数v1.1.0版本 测试环境为例演示使用 wiremock-py 对楼层客流分布和店铺客流分布两张图分布在3种场景下的测试方法 先确定本地浏览器能过正常访问 http://mall-data.com:9012 准备 克隆代码 git clone http://git.dtwave-inc.com:30000/baomi.wbm/wiremock-py.git 安装依赖 cd wiremock-py pip install -r requirements.txt npm install mockjs 生成目录 python mock.py -g "demo" wiremock-py git:(master) python mock.py -g "demo" DEBUG:root:mockdir=, scene=, target=, proxy_port=5506, generate=demo, wiremock=False, rewrite=False DEBUG:root:正在生成目录 /Users/wangbaomi/autotest/wiremock-py/demo DEBUG:root:创建目录成功: demo DEBUG:root:创建目录成功: demo/js DEBUG:root:创建目录成功: demo/json DEBUG:root:创建目录成功: demo/python DEBUG:root:创建目录成功: demo/wiremock DEBUG:root:创建文件成功: demo/mappings.json DEBUG:root:生成目录完成: /Users/wangbaomi/autotest/wiremock-py/demo 填写 mappings.json、json、python、js 数据 mappings.json 中填写内容: [ { "response": { "default": { "proxyBaseUrl": "target" } }, "mapping_name": "request url not start with /api", "request": { "method": "ANY", "urlPattern": "/(?!api).*" } }, { "mapping_name": "楼层客流分布", "request": { "urlPattern": "/api/v1/mall_data/customer_flow/every_floor\\?(.*)", "method": "POST" }, "response": { "default": { "proxyBaseUrl": "target" }, "测试场景1": { "bodyFileName": { "json": "楼层客流分布.json" } }, "测试场景2": { "bodyFileName": { "python": "楼层客流分布.py", "python_args": "测试场景2" } }, "测试场景3": { "bodyFileName": { "js": "楼层客流分布.js" } } } }, { "mapping_name": "店铺客流分布", "request": { "urlPattern": "/api/v1/mall_data/customer_flow/every_shop\\?(.*)", "method": "POST" }, "response": { "default": { "proxyBaseUrl": "target" }, "测试场景1": { "bodyFileName": { "js": "店铺客流分布.js" } }, "测试场景2": { "bodyFileName": { "json": "店铺客流分布.json" } }, "测试场景3": { "bodyFileName": { "python": "店铺客流分布.py", "python_args": "测试场景3" } } } } ] js 文件夹中新建店铺客流分布.js文件, 内容为: var r = { "success": true, "code": null, "message": null, "content": { "meta": {}, "multi": { "group": [ { "id": "rank", "name": "排名", "value": [ 1, 2, 3, 4 ] } ], "result": [ { "id": "the_shop", "name": "店铺", "value": [ "店铺1", "店铺2", "店铺3", "第4个店铺" ] }, { "id": "customer_count", "name": "人数", "value": [ 10, 100, 1000, 3242 ] } ] }, "single": [] } }; console.log(JSON.stringify(r)); js 文件夹中新建楼层客流分布.js文件, 内容为: var r = { "success": true, "code": null, "message": null, "content": {"meta": {}, "multi": { "group": [ { "id": "the_floor", "name": "楼层", "value": [ "-1楼", "1楼", "2楼", "3楼", ] } ], "result": [ { "id": "customer_count", "name": "人数", "value": [ 100, 1000, 5000, 567 ] } ] }, "single": [] } }; console.log(JSON.stringify(r)); json 文件夹中新建店铺客流分布.json, 内容为: { "success": true, "code": null, "message": null, "content": { "meta": {}, "multi": {

优秀的个人博客,低调大师

时间处理工具

package com.skjd.util; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; public class DateUtil { private final static SimpleDateFormat sdfYear = new SimpleDateFormat("yyyy"); private final static SimpleDateFormat sdfDay = new SimpleDateFormat( "yyyy-MM-dd"); private final static SimpleDateFormat sdfMouth = new SimpleDateFormat( "yyyy-MM"); private final static SimpleDateFormat sdfDays = new SimpleDateFormat( "yyyyMMdd"); private final static SimpleDateFormat sdfTime = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); public static final String DATE_FORMAT_FULL = "yyyy-MM-dd HH:mm:ss"; public static final String DATE_FORMAT_FULL_MSEL = "yyyyMMddHHmmssSSSS"; /** * 获取YYYY格式 * * @return */ public static String getYear() { return sdfYear.format(new Date()); } /** * 获取YYYY-MM-DD格式 * * @return */ public static String getDay() { return sdfDay.format(new Date()); } /** * 获取YYYYMMDD格式 * * @return */ public static String getDays(){ return sdfDays.format(new Date()); } /** * 获取YYYY-MM-DD HH:mm:ss格式 * * @return */ public static String getTime() { return sdfTime.format(new Date()); } /** * 传入一个时间字符串 * 判断此时间在晚上23点到凌晨6点经过了多久分钟 * YYYY-MM-DD HH:mm:ss */ public static int getNightSum( String s) { Date date = fomatDate(s); String str = sdfTime.format(date); String str2 = str.substring(11, 13); int i = Integer.parseInt(str2); if(i>=23){ return Integer.parseInt(str.substring(14, 16)); }else if(i<6){ i=6-i-1; int j=60-Integer.parseInt(str.substring(14, 16)); return i*60+j; }else { return 0; } } /** * @Title: compareDate * @Description: TODO(日期比较,如果s>=e 返回true 否则返回false) * @param s * @param e * @return boolean * @throws * @author luguosui */ public static boolean compareDate(String s, String e) { if(fomatDate(s)==null||fomatDate(e)==null){ return false; } return fomatDate(s).getTime() >=fomatDate(e).getTime(); } /** * 得到时间戳 * * @param null * @return String 当前日期时间戳(yyyyMMddHHmmssSSSS) */ public static String getTimeStamp() { try { Calendar now = Calendar.getInstance(TimeZone.getDefault()); SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_FULL_MSEL); sdf.setTimeZone(TimeZone.getDefault()); return (sdf.format(now.getTime())); } catch (Exception e) { return getCurDateTime(); // 如果无法转化,则返回默认格式的时间。 } } /** * 得到当前日期 * * @return String 当前日期 yyyy-MM-dd HH:mm:ss格式 */ public static String getCurDateTime() { Calendar now = Calendar.getInstance(TimeZone.getDefault()); // String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; DateFormat sdf = new SimpleDateFormat(DATE_FORMAT_FULL); sdf.setTimeZone(TimeZone.getDefault()); return (sdf.format(now.getTime())); } /** * 格式化日期 * * @return */ public static Date fomatDate(String date) { DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { return fmt.parse(date); } catch (ParseException e) { e.printStackTrace(); return null; } } /** * 校验日期是否合法 * * @return */ public static boolean isValidDate(String s) { DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); try { fmt.parse(s); return true; } catch (Exception e) { // 如果throw java.text.ParseException或者NullPointerException,就说明格式不对 return false; } } public static int getDiffYear(String startTime,String endTime) { DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); try { long aa=0; int years=(int) (((fmt.parse(endTime).getTime()-fmt.parse(startTime).getTime())/ (1000 * 60 * 60 * 24))/365); return years; } catch (Exception e) { // 如果throw java.text.ParseException或者NullPointerException,就说明格式不对 return 0; } } /** * <li>功能描述:时间相减得到天数 * @param beginDateStr * @param endDateStr * @return * long * @author Administrator */ public static long getDaySub(String beginDateStr,String endDateStr){ long day=0; java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd"); java.util.Date beginDate = null; java.util.Date endDate = null; try { beginDate = format.parse(beginDateStr); endDate= format.parse(endDateStr); } catch (ParseException e) { e.printStackTrace(); } day=(endDate.getTime()-beginDate.getTime())/(24*60*60*1000); //System.out.println("相隔的天数="+day); return day; } /** * 得到n天之后的日期 * @param days * @return */ public static String getAfterDayDate(String days) { int daysInt = Integer.parseInt(days); Calendar canlendar = Calendar.getInstance(); // java.util包 canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动 Date date = canlendar.getTime(); SimpleDateFormat sdfd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = sdfd.format(date); return dateStr; } /** * 得到n天之后是周几 * @param days * @return */ public static String getAfterDayWeek(String days) { int daysInt = Integer.parseInt(days); Calendar canlendar = Calendar.getInstance(); // java.util包 canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动 Date date = canlendar.getTime(); SimpleDateFormat sdf = new SimpleDateFormat("E"); String dateStr = sdf.format(date); return dateStr; } /** * 将日期对象转为时间戳字符串 * @param time * @return * @throws ParseException */ public static String dateutil(String time) throws ParseException{ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); Date date = format.parse(time); time = ""+date.getTime(); return time; } /** * 将日期对象转为时间戳 * @param time * @return * @throws ParseException */ public static Long dateutilToLong(String time) throws ParseException{ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); Date date = format.parse(time); Long time2 = date.getTime(); return time2; } public static String parseDateToStr(Date thedate) { // String format = "yyyy-MM-dd"; return parseDateToString(thedate, DATE_FORMAT_FULL); } public static String parseDateToString(Date thedate, String format) { DateFormat df = new SimpleDateFormat(format); if (thedate != null) { return df.format(thedate.getTime()); } return ""; } /** * 获取当前时间前一个月的时间 * @param args */ public static String getDateLastMouth(){ Calendar c = Calendar.getInstance(); c.setTime(new Date()); c.add(Calendar.MONTH, -1); Date m = c.getTime(); return sdfTime.format(m); } /** * 获取当月第一天的日期 * @return */ public static String getMouthNoOne(){ return sdfMouth.format(new Date())+"-01 00:00"; } public static void main(String[] args) { boolean b = compareDate("2018-08-18 00:00", DateUtil.getTime()); System.out.println(b); } }

优秀的个人博客,低调大师

Android 日志工具

1). 实现效果图 效果图.png 2). 实现思路 绘制边框 打印线程名称 打印堆栈信息 打印消息体 3). 边框绘制 边框实际上也是以字符串的形式打印 4). 打印线程信息 Log.println(priority, mTag, HORIZONTAL_LINE + "Thread: " + Thread.currentThread().getName()); 5). 打印堆栈信息 builder.append(HORIZONTAL_LINE).append("").append(level).append(element.getClassName().substring(element.getClassName().lastIndexOf(".") + 1)) .append(".").append(element.getMethodName()).append(" ").append(" (").append(element.getFileName()) .append(":").append(element.getLineNumber()).append(")"); 6). 打印消息体 // 获取消息总长度 int tLen = msg.length(); // 获取消息长度打印长度的倍数 int multiple = tLen / LENGTH + 1; // 创建临时变量 String tmp; // 遍历倍数 for (int i = 0; i < multiple; i++) { // 获取字符内容 if (i != multiple - 1) { // 获取指定范围的数据 tmp = msg.substring(i * LENGTH, (i + 1) * LENGTH); } else { // 最后一段数据 tmp = msg.substring(i * LENGTH); } // 打印消息 Log.println(priority, mTag, HORIZONTAL_LINE + tmp); } 7). 代码下载 8). Gradle依赖 compile 'com.mazaiting:log:1.0.0'

优秀的个人博客,低调大师

【Android】日志工具Log类

android.util.Log类 android.util.Log常用的方法有以下5个:Log.v(), Log.d() ,Log.i() ,Log.w() 以及 Log.e() 。根据首字母对应VERBOSE,DEBUG,INFO, WARN,ERROR。 Log.v 的输出颜色为黑色的,任何消息都会输出,这里的v代表verbose啰嗦的意思。 Log.d的输出颜色为蓝色的,仅输出debug调试的意思,但他会输出上层的信息,过滤起来可以通过DDMS的Logcat标签来选择。 Log.i的输出颜色为绿色,一般提示性的消息information,它不会输出Log.v和Log.d的信息,但会显示i、w和e的信息。 Log.w的输出颜色为橙色,可以看作为warning警告,一般需要我们注意优化Android代码,同时选择它后还会输出Log.e的信息。 Log.e的输出颜色为红色,可以想到error错误,这里仅显示红色的错误信息,这些错误就需要我们认真的分析,查看栈的信息了。 1. 常规用法 Log.v(TAG,"一条啰嗦日志"); Log.d(TAG,"一条调试日志"); Log.i(TAG,"一条提示日志"); Log.w(TAG,"一条警告日志"); Log.e(TAG,"一条错误日志"); 其中TAG为字符串。用于标记。同一个应用TAG基本可以成一样。并且,应用打包发布时,最好把全部Log注释掉,否则将产生不必要的资源消耗(虽然很小)。 2. 封装Log public class L { private static String TAG = "WZM"; // 开关 private static boolean debug = true; private L() { } public static void setTAG(String TAG) { L.TAG = TAG; } public static void setDebug(boolean debug) { L.debug = debug; } public static void v(String msg) { if (debug) Log.v(TAG, msg); } public static void d(String msg) { if (debug) Log.d(TAG, msg); } public static void i(String msg) { if (debug) Log.i(TAG, msg); } public static void w(String msg) { if (debug) Log.w(TAG, msg); } public static void e(String msg) { if (debug) Log.e(TAG, msg); } } 使用时: L.v("一条啰嗦日志"); L.d("一条调试日志"); L.i("一条提示日志"); L.w("一条警告日志"); L.e("一条错误日志"); 并且,可在Application中设置开关(是否输出日志)或TAG(默认WZM) public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); L.setTAG("TAG"); L.setDebug(false); } } 打包发布时,debug设为false即可。 配一张图 其他 还有对Toast的简单封装:Android 封装Toast

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册