当前位置:   article > 正文

ltp 使用_ltp使用

ltp使用

https://blog.csdn.net/yuanlaijike/article/details/78068331

 

            1.1 LTP介绍
                1.1.1 功能测试
                1.1.2 回归测试
                1.1.3 压力测试
            1.2 LTP环境部署
                1.2.1 下载LTP
                1.2.2 部署LTP
            1.3 目录结构
                1.3.1 源码包
                1.3.2 安装包
            1.4 测试框架
                1.4.1 整体测试流程
                1.4.2 测试用例执行流程
                1.4.3 测试库
            1.5 测试执行
                1.5.1 整体测试
                1.5.2 单独测试
                    1.5.2.1 安装包测试
                    1.5.2.2 源码包测试
            1.6 牛刀小试
                1.6.1 验证sqrt() 函数
                1.6.2 验证echo命令

    源码地址:https://github.com/jitwxs/blog_sample

1.1 LTP介绍

LTP(Linux Test Project),是基于GPL协议的开源社区合作项目。2000年由SGI发起,IBM、OSDL和Bull等公司共同参与,2001年后由SUSE、富士通、Red Hat、Oracle共同开发和维护。

通过功能测试、压力测试和回归测试来验证Linux系统的可靠性、稳定性和健壮性。整个项目约4000个测试用例,绝大部分用例采用C或Shell。

LTP不仅测试内核,还测试整体系统环境,对功能执行失败时的返回和处理也进行测试。
1.1.1 功能测试

主要对man pages中1、8命令和2系统调用所描述的功能进行验证。
1.1.2 回归测试

修改了旧代码后,重新进行测试已确认修改没有引入新的错误或导致其他代码产生错误。
1.1.3 压力测试

测试系统功能特性再大负荷压力下的稳定性和可靠性。
1.2 LTP环境部署
1.2.1 下载LTP

LTP项目目前位于GitHub,项目地址:https://github.com/linux-test-project/ltp

获取最新版可以执行以下命令:git clone https://github.com/linux-test-project/ltp.git
1.2.2 部署LTP

首先执行下面命令安装相关软件包(已安装可跳过):

  1. #CentOS
  2. sudo yum install autoconf automake autotools-dev m4
  3. #Ubuntu
  4. sudo apt-get install autoconf automake autotools-dev m4

在上节中我将ltp项目下载到了wxs用户的家目录(/home/wxs)下,如图所示:

  1. [wxs@bogon ~]$ cd ltp/
  2. [wxs@bogon ltp]$ ls
  3. aclocal.m4 configure.ac INSTALL pan testcases
  4. autom4te.cache confLkNw6U install-sh README.kernel_config testscripts
  5. confc20wzw COPYING lib README.md TODO
  6. config.guess doc ltpmenu runltp tools
  7. config.log execltp m4 runltplite.sh utils
  8. config.status execltp.in Makefile runtest ver_linux
  9. config.sub IDcheck.sh Makefile.release scenario_groups Version
  10. configure include missing scripts VERSION

 

进入ltp目录:cd ltp

生成自动工具:make autotools

系统环境配置:./configure

编译:make -j$(getconf_NPROCESSORS_ONLN)

安装:sudo make install

依次执行以上命令后,LTP已经被正确安装到你的Linux系统中,默认安装位于/opt/ltp/。

  1. [wxs@bogon ltp]$ cd /opt/ltp/
  2. [wxs@bogon ltp]$ ls
  3. bin runltp runtest share testscripts Version
  4. IDcheck.sh runltplite.sh scenario_groups testcases ver_linux

需要注意的是,我们通过git clone命令下载的位于home目录下的ltp文件夹为ltp源码文件夹,我将在后文简称为源码包。

通过执行一系列命令安装到/opt目录下的ltp文件夹为ltp安装文件夹,我将在后文简称为安装包。
1.3 目录结构
1.3.1 源码包

LTP源码包目录结构描述如下:
名称     说明
INSTALL     LTP安装配置指导文档
README     LTP介绍
CREDITS     记录对LTP有很大贡献的人
COPYING     GNU公开许可证
ChangeLog     描述版本变化
ltpmenu     规划执行LTP的图形化界面接口
Makefile     LTP顶层目录的Makefile,负责编译安装pan、testcases和tools
runalltests.sh     顺序运行全部测试用例并且报告结果的脚本
doc/*     工程文档包含工具和库函数使用手册,描述各种测试
include/*     通用的头文件目录
lib/*     通用的函数目录
testcases/*     包含在LTP下运行和bin目录下的所有测试用例和链接
testscripts/*     存放分组的测试脚本
runtest/*     为自动化测试提供命令列表
pan/*     测试的驱动装置,具备随机和并行测试的能力
scratch/*     存放零碎测试
tools/*     存放自动化测试脚本和辅助工具

LTP测试套件包含以下内容:

  1. [wxs@bogon ~]$ cd ltp/testcases/
  2. [wxs@bogon testcases]$ ls
  3. commands demoA kernel Makefile network realtime
  4. cve kdump lib misc open_posix_testsuite

 

目录结构描述如下:
名称     说明
commands     常用命令测试
kernel     内核模块及其相关模块
kdump     内核现崩溃转储测试
network     网络测试
realtime     系统实时性测试
open_posix_testsuite     posix标准测试
misc     崩溃、核心转出、浮点运算等测试
1.3.2 安装包

LTP安装包目录结构描述如下:
名称     说明
bin     存放LTP测试的一些辅助脚本
results     测试结果默认存储目录
testcases     测试项集
output     测试日志默认存储目录
share     脚本使用说明目录
runtest     测试驱动(用于链接testscripts内的测试脚本和testcases测试项目)
lib     通用的库函数目录
1.4 测试框架
1.4.1 整体测试流程

ltp安装包根目录下的runltp脚本是LTP自动测试系统的入口,其提供了一系列参数选项,允许用户设定测试环境制定测试集、控制测试结果输出方式和路径等,运行runltp会生成指定的测试列表并调用测试驱动PAN来开始测试,待执行完毕后根据PAN返回的结果来生成报告。

PAN是LTP的一组测试驱动程序,负责实际测试的执行,根据runltp传递的参数和测试列表来依次执行测试,输出执行过程中的详细信息,对每个测试用例的执行结果进行统计,并将整体测试结果返回给runltp。

整体测试流程
1.4.2 测试用例执行流程

测试用例执行流程

测试结果的输出类型如下:
Type     Description
BROK     程序执行中途发生错误而使测试遭到破坏
CONF     测试环境不满足而跳过执行
WARN     测试中途发生异常
INFO     输出通用测试信息
PASS     测试成功
FAIL     测试失败
1.4.3 测试库

LTP目前测试库存在新旧测试库交替的情况,本文均采用新测试库的框架,具体的更新说明可以参考更新文档An update on the Linux Test Project。

测试库
Old Library     New Library
测试用例在执行时调用测试库的API     在测试库维护的子进程中回调测试用例
setup()中逐一调用API完成测试准备     setup()中测试属性以结构体变量定义
main()定义在每个测试用例当中     main()定义在测试库中
cleanup()中不能调用SAFE函数     cleanup()中允许调用SAFE函数

这里以umount02为例,比较新旧框架的区别:

旧框架代码:

 

  1. //Example using the old LTP library
  2. //https://lwn.net/Articles/708250/
  3. #include <errno.h>
  4. #include <sys/mount.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <sys/fcntl.h>
  8. #include <pwd.h>
  9. #include "test.h"
  10. #include "safe_macros.h"
  11. static void setup(void);
  12. static void cleanup(void);
  13. char *TCID = "umount02";
  14. #define DIR_MODE S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH
  15. #define FILE_MODE S_IRWXU | S_IRWXG | S_IRWXO
  16. #define MNTPOINT "mntpoint"
  17. static char long_path[PATH_MAX + 2];
  18. static int mount_flag;
  19. static int fd;
  20. static const char *device;
  21. static struct test_case_t {
  22. char *err_desc;
  23. char *mntpoint;
  24. int exp_errno;
  25. char *exp_retval;
  26. } testcases[] = {
  27. {"Already mounted/busy", MNTPOINT, EBUSY, "EBUSY"},
  28. {"Invalid address space", NULL, EFAULT, "EFAULT"},
  29. {"Directory not found", "nonexistent", ENOENT, "ENOENT"},
  30. {"Invalid device", "./", EINVAL, "EINVAL"},
  31. {"Pathname too long", long_path, ENAMETOOLONG, "ENAMETOOLONG"}
  32. };
  33. int TST_TOTAL = ARRAY_SIZE(testcases);
  34. int main(int ac, char **av)
  35. {
  36. int lc, i;
  37. tst_parse_opts(ac, av, NULL, NULL);
  38. setup();
  39. for (lc = 0; TEST_LOOPING(lc); lc++) {
  40. tst_count = 0;
  41. for (i = 0; i < TST_TOTAL; ++i) {
  42. TEST(umount(testcases[i].mntpoint));
  43. if ((TEST_RETURN == -1) && (TEST_ERRNO == testcases[i].exp_errno)) {
  44. tst_resm(TPASS, "umount(2) expected failure; "
  45. "Got errno - %s : %s",
  46. testcases[i].exp_retval,
  47. testcases[i].err_desc);
  48. } else {
  49. tst_resm(TFAIL, "umount(2) failed to produce "
  50. "expected error; %d, errno:%s got %d",
  51. testcases[i].exp_errno,
  52. testcases[i].exp_retval, TEST_ERRNO);
  53. }
  54. }
  55. }
  56. cleanup();
  57. tst_exit();
  58. }
  59. static void setup(void)
  60. {
  61. const char *fs_type;
  62. tst_sig(FORK, DEF_HANDLER, cleanup);
  63. tst_require_root();
  64. tst_tmpdir();
  65. fs_type = tst_dev_fs_type();
  66. device = tst_acquire_device(cleanup);
  67. if (!device)
  68. tst_brkm(TCONF, cleanup, "Failed to obtain block device");
  69. tst_mkfs(cleanup, device, fs_type, NULL, NULL);
  70. memset(long_path, 'a', PATH_MAX + 1);
  71. SAFE_MKDIR(cleanup, MNTPOINT, DIR_MODE);
  72. if (mount(device, MNTPOINT, fs_type, 0, NULL))
  73. tst_brkm(TBROK | TERRNO, cleanup, "mount() failed");
  74. mount_flag = 1;
  75. fd = SAFE_OPEN(cleanup, MNTPOINT "/file", O_CREAT | O_RDWR);
  76. TEST_PAUSE;
  77. }
  78. static void cleanup(void)
  79. {
  80. if (fd > 0 && close(fd))
  81. tst_resm(TWARN | TERRNO, "Failed to close file");
  82. if (mount_flag && tst_umount(MNTPOINT))
  83. tst_resm(TWARN | TERRNO, "umount() failed");
  84. if (device)
  85. tst_release_device(device);
  86. tst_rmdir();
  87. }

新框架代码:

 

  1. //Example using the new LTP library
  2. //https://lwn.net/Articles/708251/
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <sys/mount.h>
  6. #include "tst_test.h"
  7. #define MNTPOINT "mntpoint"
  8. static char long_path[PATH_MAX + 2];
  9. static int mount_flag;
  10. static int fd;
  11. static struct tcase {
  12. const char *err_desc;
  13. const char *mntpoint;
  14. int exp_errno;
  15. } tcases[] = {
  16. {"Already mounted/busy", MNTPOINT, EBUSY},
  17. {"Invalid address", NULL, EFAULT},
  18. {"Directory not found", "nonexistent", ENOENT},
  19. {"Invalid device", "./", EINVAL},
  20. {"Pathname too long", long_path, ENAMETOOLONG}
  21. };
  22. static void verify_umount(unsigned int n)
  23. {
  24. struct tcase *tc = &tcases[n];
  25. TEST(umount(tc->mntpoint));
  26. if (TEST_RETURN != -1) {
  27. tst_res(TFAIL, "umount() succeeds unexpectedly");
  28. return;
  29. }
  30. if (tc->exp_errno != TEST_ERRNO) {
  31. tst_res(TFAIL | TTERRNO, "umount() should fail with %s",
  32. tst_strerrno(tc->exp_errno));
  33. return;
  34. }
  35. tst_res(TPASS | TTERRNO, "umount() fails as expected: %s",
  36. tc->err_desc);
  37. }
  38. static void setup(void)
  39. {
  40. memset(long_path, 'a', PATH_MAX + 1);
  41. SAFE_MKFS(tst_device->dev, tst_device->fs_type, NULL, NULL);
  42. SAFE_MKDIR(MNTPOINT, 0775);
  43. SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, NULL);
  44. mount_flag = 1;
  45. fd = SAFE_CREAT(MNTPOINT "/file", 0777);
  46. }
  47. static void cleanup(void)
  48. {
  49. if (fd > 0 && close(fd))
  50. tst_res(TWARN | TERRNO, "Failed to close file");
  51. if (mount_flag)
  52. tst_umount(MNTPOINT);
  53. }
  54. static struct tst_test test = {
  55. .tid = "umount02",
  56. .tcnt = ARRAY_SIZE(tcases),
  57. .needs_root = 1,
  58. .needs_tmpdir = 1,
  59. .needs_device = 1,
  60. .setup = setup,
  61. .cleanup = cleanup,
  62. .test = verify_umount,
  63. };

1.5 测试执行
1.5.1 整体测试

我们可以测试所有的测试集,直接运行runltp命令将测试ltp/scenario_groups/default中的所有测试集,一次测试约2~3小时。

  1. [wxs@bogon ltp]$ cd /opt/ltp
  2. [wxs@bogon ltp]$ sudo ./runltp

 

当然我们可以只测试某个测试集,测试集可以在ltp/runtest/下查看。

  1. [wxs@bogon ltp]$ ls runtest/
  2. admin_tools ipc net_stress.ipsec_udp
  3. can kernel_misc net_stress.multicast
  4. cap_bounds ltp-aiodio.part1 net_stress.route
  5. commands ltp-aiodio.part2 net.tcp_cmds
  6. connectors ltp-aiodio.part3 net.tirpc_tests
  7. containers ltp-aiodio.part4 network_commands
  8. controllers ltp-aio-stress.part1 nptl
  9. cpuhotplug ltp-aio-stress.part2 numa
  10. crashme ltplite pipes
  11. cve lvm.part1 power_management_tests
  12. dio lvm.part2 power_management_tests_exclusive
  13. dma_thread_diotest math pty
  14. fcntl-locktests mm quickhit
  15. filecaps modules sched
  16. fs net.features scsi_debug.part1
  17. fs_bind net.ipv6 securebits
  18. fs_ext4 net.ipv6_lib smack
  19. fs_perms_simple net.multicast stress.part1
  20. fs_readonly net.nfs stress.part2
  21. fsx net.rpc stress.part3
  22. hugetlb net.rpc_tests syscalls
  23. hyperthreading net.sctp syscalls-ipc
  24. ima net_stress.appl timers
  25. input net_stress.broken_ip tpm_tools
  26. io net_stress.interface tracing
  27. io_cd net_stress.ipsec_icmp
  28. io_floppy net_stress.ipsec_tcp
  29. [wxs@bogon ltp]$ sudo ./runltp -f modules

 

需要注意的是,如果我们测试某个测试集,runltp需要指定-f参数。
1.5.2 单独测试

如果我们不想测试某个测试集,只想测试某个单独的测试,可以采用安装包测试或者源码包测试。下面以access01为例,讲解单独测试。
1.5.2.1 安装包测试

进入安装包,执行以下命令即可。

  1. [wxs@bogon ltp]$ cd /opt/ltp/
  2. [wxs@bogon ltp]$ sudo ./runltp -s access01

 

需要注意的是,如果我们测试某个测试,runltp需要指定-s参数。
1.5.2.2 源码包测试

进入源码包,找到access01的位置,直接执行./access01即可。

  1. [wxs@bogon access]$ cd ~/ltp/testcases/kernel/syscalls/access/
  2. [wxs@bogon access]$ sudo ./access01

 

我们看到access01位于testcases目录下,实际上testcases目录下每个文件都是一个完整的可执行程序,可以在编译后的源码路径直接执行。
1.6 牛刀小试

本节中牵扯到的项目均位于源码包testcases下自建的demoA文件夹中。测试方法均采用测试集测试(C实现的测试用例可以采用源码测试和测试集测试,Shell实现的测试用例只可以采用测试集测试)。

  1. [wxs@bogon ~]$ cd ~/ltp/testcases/
  2. [wxs@bogon testcases]$ mkdir demoA
  3. [wxs@bogon testcases]$ cd demoA/
  4. [wxs@bogon demoA]$ pwd
  5. /home/wxs/ltp/testcases/demoA

 

1.6.1 验证sqrt() 函数

参考1.3.3节中给出的示例代码,编写测试用例sqrt.c:

  1. #include <errno.h>
  2. #include <string.h>
  3. #include <sys/mount.h>
  4. #include "tst_test.h"
  5. static struct tcase {
  6. const int input;
  7. const int output;
  8. } tcases[] = {
  9. {-1,1},
  10. {9,3}
  11. };
  12. static void testSqrt(unsigned int n){
  13. struct tcase *tc = &tcases[n];
  14. TEST(sqrt(tc->input));
  15. if (TEST_RETURN != tc->output) {
  16. tst_res(TFAIL, "sqrt() failed");
  17. return;
  18. }
  19. tst_res(TPASS, "sqrt() succeeds");
  20. }
  21. static struct tst_test test = {
  22. .tid = "testSqrt",
  23. .tcnt = ARRAY_SIZE(tcases),
  24. .test = testSqrt,
  25. };

参考testcases目录下的其他Makefile文件,编写Makefile:

  1. top_srcdir ?= ../..
  2. include $(top_srcdir)/include/mk/testcases.mk
  3. include $(top_srcdir)/include/mk/generic_leaf_target.mk
  4. sqrt: LDLIBS += -lm

 

需要注意的是,这里的top_srcdir指的是ltp目录,因为demoA目录位于ltp目录的内两层,所以使用了../..。

还需要注意的是,要想成功使用sqrt命令必须附加-lm参数,在Makefile中已经体现这一点。

执行make命令,生成可执行文件sqrt:

[wxs@bogon demoA]$ make
make -C "/home/wxs/ltp/lib" -f "/home/wxs/ltp/lib/Makefile" all
make[1]: 进入目录“/home/wxs/ltp/lib”
make[2]: 进入目录“/home/wxs/ltp/lib/newlib_tests”
make[2]: 对“all”无需做任何事。
make[2]: 离开目录“/home/wxs/ltp/lib/newlib_tests”
make[2]: 进入目录“/home/wxs/ltp/lib/tests”
make[2]: 对“all”无需做任何事。
make[2]: 离开目录“/home/wxs/ltp/lib/tests”
make[1]: 离开目录“/home/wxs/ltp/lib”
gcc -g -O2 -g -O2 -fno-strict-aliasing -pipe -Wall -W -Wold-style-definition -D_FORTIFY_SOURCE=2 -I../../include -I../../include -I../../include/old/   -L../../lib  sqrt.c   -lltp -lm -o sqrt
sqrt.c: 在函数‘testSqrt’中:
sqrt.c:17:2: 警告:隐式声明函数‘sqrt’ [-Wimplicit-function-declaration]
  TEST(sqrt(tc->input));
  ^
In file included from sqrt.c:4:0:
sqrt.c:17:7: 警告:隐式声明与内建函数‘sqrt’不兼容 [默认启用]
  TEST(sqrt(tc->input));
       ^
../../include/tst_test.h:176:17: 附注:in definition of macro ‘TEST’
   TEST_RETURN = SCALL; \
                 ^

这里可以直接执行命令./sqrt来运行这个测试用例,但是对于下节的shell测试用例就不能这样做了,本节我都将它写在自定义测试集中。

首先复制可执行文件sqrt到安装包的testcases/bin/目录下:

[wxs@bogon demoA]$ sudo cp sqrt /opt/ltp/testcases/bin/

 

然后进入安装包的runtest目录下,编写自定义测试用例集demoA:

  1. [wxs@bogon runtest]$ cat demoA
  2. sqrt sqrt

测试用例集中每个测试用例包括两部分:前部分为昵称,后部分为testcases/bin/目录下的文件名,中间用空格分隔。

进入上层目录,执行整体测试命令:

 

  1. [wxs@bogon runtest]$ cd ..
  2. [wxs@bogon ltp]$ sudo ./runltp -f demoA

执行后程序会输出一大段,我们只需关心最重要的部分。我定义从<<<test_start>>>到<<<test_end>>>中间的内容为test体,后文均以此称呼:···
 

  1. <<<test_start>>>
  2. tag=sqrt stime=1506106767
  3. cmdline="sqrt"
  4. contacts=""
  5. analysis=exit
  6. <<<test_output>>>
  7. tst_test.c:915: INFO: Timeout per run is 0h 05m 00s
  8. incrementing stop
  9. sqrt.c:20: FAIL: sqrt() failed
  10. sqrt.c:23: PASS: sqrt() succeeds
  11. Summary:
  12. passed 1
  13. failed 1
  14. skipped 0
  15. warnings 0
  16. <<<execution_status>>>
  17. initiation_status="ok"
  18. duration=0 termination_type=exited termination_id=1 corefile=no
  19. cutime=0 cstime=0
  20. <<<test_end>>>

可以看到,两个测试点一个FAIL,一个PASS。对照sqrt.c文件中给出的测试数据,发现测试没有问题。至此,一个简单的LTP测试用例就完成了。
1.6.2 验证echo命令

重新回到demoA文件夹中,编写echo.sh文件,代码可以参考源码包中testcases/commands中其他的测试用例:#测试函数执行次数
TST_CNT=1
#测试用例启动函数

  1. TST_TESTFUNC=do_test
  2. . tst_test.sh
  3. echo_test()
  4. {
  5. local std_in=$1
  6. local echo_cmd=$(echo $std_in > a.out)
  7. local echo_res=$std_in
  8. local cat_res=$(cat a.out)
  9. if [ $echo_res = $cat_res ]
  10. then
  11. tst_res TPASS "$echo_cmd sucessed"
  12. else
  13. tst_res TFAIL "$echo_cmd failed"
  14. fi
  15. }
  16. do_test()
  17. {
  18. echo_test "hello\tworld"
  19. }
  20. tst_run

为echo.sh添加可执行权限,并复制到安装包中的testcases/bin/目录中。

这里一定要注意,不可以在当前路径直接./echo.sh执行测试用例!!

  1. [wxs@bogon demoA]$ pwd
  2. /home/wxs/ltp/testcases/demoA
  3. [wxs@bogon demoA]$ sudo chmod +x echo.sh
  4. [wxs@bogon demoA]$ sudo cp echo.sh /opt/ltp/testcases/bin/

 

修改上一节中编写的demoA测试用例集,将echo.sh测试用例添加进去:

  1. [wxs@bogon demoA]$ cd /opt/ltp/runtest/
  2. [wxs@bogon runtest]$ cat demoA
  3. sqrt sqrt
  4. echo echo.sh

注意了,此时测试用例集中包含了两个测试用例,那么后面运行该测试用例集将会执行这两个测试用例。

进入上层目录,执行整体测试命令:

  1. [wxs@bogon runtest]$ cd ..
  2. [wxs@bogon ltp]$ sudo ./runltp -f demoA

结果输出依然很多,我们依然只看最关键的部分:

··· <<<test_output>>> tst_test.c:915: INFO: Timeout per run is 0h 05m 00s sqrt.c:20: FAIL: sqrt() failed sqrt.c:23: PASS: sqrt() succeeds Summary: passed 1 failed 1 skipped 0 warnings 0 <<<execution_status>>> initiation_status="ok" duration=1 termination_type=exited termination_id=1 corefile=no cutime=0 cstime=0 <<<test_end>>> <<<test_start>>> tag=echo stime=1506107663 cmdline="echo.sh" contacts="" analysis=exit <<<test_output>>> incrementing stop 1 TPASS: sucessed Summary: passed 1 failed 0 skipped 0 warnings 0 <<<execution_status>>> initiation_status="ok" duration=0 termination_type=exited termination_id=0 corefile=no cutime=2 cstime=5 <<<test_end>>> ···

 

 

本次测试输出包含两个test体,第一个test体我们在上一节已经说过了,我们直接看第二个test体。该体中共计有1个测试点,状态为TPASS。

对照echo.sh中的测试点,发现没有问题。

至此你已经学会了最基本的创建C和Shell的测试用例以及整体测试的方法,本章内容你已经完成了。
 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/316326
推荐阅读
相关标签
  

闽ICP备14008679号