GUI测试全能手:Java中TestFX、FEST Swing、Robot类等库的终极对比
前言
在当今软件开发的领域中,图形用户界面(GUI)测试与自动化是确保应用程序质量和稳定性的关键环节。Java作为一种广泛应用的编程语言,在GUI开发中占据重要地位。本文将深入探讨多个Java库,涵盖了从Swing到图像识别再到系统级别的操作,为开发人员提供全方位的GUI测试与自动化解决方案。
欢迎订阅专栏:Java万花筒
文章目录
1. TestFX (JavaFX应用程序测试库)
1.1 基本介绍
TestFX是专为JavaFX应用程序设计的UI测试库,它提供了一套强大的工具,可用于模拟用户与JavaFX应用程序的交互,以及验证应用程序的状态。它允许测试人员编写清晰、可读的测试代码,以确保JavaFX应用程序的可靠性和稳定性。
1.2 安装与配置
要使用TestFX,首先需要在项目中添加相应的依赖。以下是使用Maven的示例:
<dependency>
<groupId>org.testfx</groupId>
<artifactId>testfx-core</artifactId>
<version>4.0.16-alpha</version>
<scope>test</scope>
</dependency>
1.3 核心功能
1.3.1 场景与节点查找
TestFX通过场景(Scene)和节点(Node)的概念来定位和操作JavaFX应用程序的元素。以下是一个简单的例子:
import javafx.scene.control.Button;
import org.junit.Test;
import org.testfx.api.FxAssert;
import org.testfx.api.FxRobot;
import org.testfx.framework.junit.ApplicationTest;
public class MyJavaFXAppTest extends ApplicationTest {
@Override
public void start(Stage primaryStage) {
Button myButton = new Button("Click Me");
myButton.setId("myButtonId");
StackPane root = new StackPane(myButton);
primaryStage.setScene(new Scene(root, 300, 200));
primaryStage.show();
}
@Test
public void testButtonClick() {
clickOn("#myButtonId");
FxAssert.verifyThat("#myButtonId", (Button b) -> b.getText().equals("Clicked"));
}
}
1.3.2 事件模拟与交互
TestFX允许模拟用户与应用程序的交互,如点击、输入等。以下是一个模拟点击按钮的示例:
import javafx.scene.control.Button;
import org.junit.Test;
import org.testfx.api.FxRobot;
import org.testfx.framework.junit.ApplicationTest;
public class MyJavaFXAppTest extends ApplicationTest {
@Override
public void start(Stage primaryStage) {
Button myButton = new Button("Click Me");
myButton.setId("myButtonId");
StackPane root = new StackPane(myButton);
primaryStage.setScene(new Scene(root, 300, 200));
primaryStage.show();
}
@Test
public void testButtonClick() {
clickOn("#myButtonId");
}
}
1.3.3 断言与验证
TestFX提供了丰富的断言方法,用于验证应用程序的状态。以下是一个简单的例子:
import javafx.scene.control.Button;
import org.junit.Test;
import org.testfx.api.FxAssert;
import org.testfx.framework.junit.ApplicationTest;
public class MyJavaFXAppTest extends ApplicationTest {
@Override
public void start(Stage primaryStage) {
Button myButton = new Button("Click Me");
myButton.setId("myButtonId");
StackPane root = new StackPane(myButton);
primaryStage.setScene(new Scene(root, 300, 200));
primaryStage.show();
}
@Test
public void testButtonExistence() {
FxAssert.verifyThat("#myButtonId", (Button b) -> b.isVisible());
}
}
2. FEST Swing (Swing GUI测试库)
2.1 概述与背景
FEST Swing是用于测试Swing界面的库,它提供了丰富的API和工具,帮助开发人员编写清晰、简洁的测试代码。它专注于提供易于理解和维护的测试用例,以确保Swing应用程序的正确性。
2.2 安装与集成
要使用FEST Swing,首先需要在项目中添加相应的依赖。以下是使用Maven的示例:
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-swing</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>
2.3 测试基本流程
2.3.1 窗体与组件操作
FEST Swing允许开发人员轻松地访问和操作Swing窗体及其组件。以下是一个简单的窗体操作示例:
import org.fest.swing.core.BasicRobot;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.FrameFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.swing.*;
public class MySwingAppTest {
private Robot robot;
private FrameFixture frame;
@Before
public void setUp() {
robot = BasicRobot.robotWithNewAwtHierarchy();
frame = new FrameFixture(robot, new MySwingApp().getMainFrame());
frame.show(); // 显示Swing窗体
}
@After
public void tearDown() {
frame.cleanUp();
}
@Test
public void testButtonClick() {
frame.button("myButton").click();
frame.label("resultLabel").requireText("Button Clicked");
}
}
2.3.2 事件触发与监听
FEST Swing支持模拟用户事件的触发和监听。以下是一个模拟按钮点击事件的示例:
import org.fest.swing.core.BasicRobot;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.FrameFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.swing.*;
public class MySwingAppTest {
private Robot robot;
private FrameFixture frame;
@Before
public void setUp() {
robot = BasicRobot.robotWithNewAwtHierarchy();
frame = new FrameFixture(robot, new MySwingApp().getMainFrame());
frame.show();
}
@After
public void tearDown() {
frame.cleanUp();
}
@Test
public void testButtonClick() {
frame.button("myButton").click();
frame.label("resultLabel").requireText("Button Clicked");
}
}
2.3.3 断言与验证
FEST Swing提供了丰富的断言方法,用于验证Swing应用程序的状态。以下是一个简单的验证标签文本的示例:
import org.fest.swing.core.BasicRobot;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.FrameFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.swing.*;
public class MySwingAppTest {
private Robot robot;
private FrameFixture frame;
@Before
public void setUp() {
robot = BasicRobot.robotWithNewAwtHierarchy();
frame = new FrameFixture(robot, new MySwingApp().getMainFrame());
frame.show();
}
@After
public void tearDown() {
frame.cleanUp();
}
@Test
public void testLabelContent() {
frame.label("myLabel").requireText("Hello, Swing!");
}
}
2.4 高级功能与扩展
2.4.1 数据驱动测试
FEST Swing支持数据驱动测试,允许开发人员通过不同的输入数据运行相同的测试用例。以下是一个简单的数据驱动测试示例:
import org.fest.swing.core.BasicRobot;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.FrameFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import javax.swing.*;
import java.util.Arrays;
import java.util.Collection;
@RunWith(Parameterized.class)
public class MySwingAppParameterizedTest {
private Robot robot;
private FrameFixture frame;
private String input;
private String expectedOutput;
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{"Input1", "ExpectedOutput1"},
{"Input2", "ExpectedOutput2"},
{"Input3", "ExpectedOutput3"}
});
}
public MySwingAppParameterizedTest(String input, String expectedOutput) {
this.input = input;
this.expectedOutput = expectedOutput;
}
@Before
public void setUp() {
robot = BasicRobot.robotWithNewAwtHierarchy();
frame = new FrameFixture(robot, new MySwingApp().getMainFrame());
frame.show();
}
@After
public void tearDown() {
frame.cleanUp();
}
@Test
public void testLabelContent() {
frame.textBox("inputTextBox").setText(input);
frame.button("processButton").click();
frame.label("outputLabel").requireText(expectedOutput);
}
}
2.4.2 并发测试
FEST Swing支持并发测试,允许开发人员同时运行多个测试用例。以下是一个简单的并发测试示例:
import org.fest.swing.core.BasicRobot;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.FrameFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.swing.*;
public class MySwingAppConcurrentTest {
private Robot robot;
private FrameFixture frame;
@Before
public void setUp() {
robot = BasicRobot.robotWithNewAwtHierarchy();
frame = new FrameFixture(robot, new MySwingApp().getMainFrame());
frame.show();
}
@After
public void tearDown() {
frame.cleanUp();
}
@Test
public void testButton1Click() {
frame.button("button1").click();
frame.label("resultLabel").requireText("Button 1 Clicked");
}
@Test
public void testButton2Click() {
frame.button("button2").click();
frame.label("resultLabel").requireText("Button 2 Clicked");
}
}
3. AssertJ Swing (Swing应用程序的断言库)
3.1 简介与定位
AssertJ Swing是一个用于Swing应用程序的断言库,旨在提供清晰、流畅的断言语法,以增强对Swing界面的验证和测试。
3.2 安装与设置
要使用AssertJ Swing,需要在项目中添加相应的依赖。以下是使用Maven的示例:
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-swing</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
3.3 常用断言方法
3.3.1 组件状态断言
AssertJ Swing提供了多种用于验证Swing组件状态的断言方法。以下是一个简单的示例:
import org.assertj.swing.core.GenericTypeMatcher;
import org.assertj.swing.fixture.FrameFixture;
import org.junit.Test;
import javax.swing.*;
public class MySwingAppAssertJTest {
@Test
public void testButtonExistence() {
FrameFixture frame = new FrameFixture(new MySwingApp().getMainFrame());
frame.button("myButton").requireVisible().requireEnabled();
}
@Test
public void testLabelText() {
FrameFixture frame = new FrameFixture(new MySwingApp().getMainFrame());
frame.label("myLabel").requireText("Hello, Swing!");
}
@Test
public void testComboBoxSelection() {
FrameFixture frame = new FrameFixture(new MySwingApp().getMainFrame());
frame.comboBox("myComboBox").selectItem("Option 1");
frame.comboBox("myComboBox").requireSelectedItem("Option 1");
}
}
3.3.2 事件与交互断言
AssertJ Swing还提供了验证Swing组件事件和交互的断言方法。以下是一个简单的事件触发断言示例:
import org.assertj.swing.core.GenericTypeMatcher;
import org.assertj.swing.fixture.FrameFixture;
import org.junit.Test;
import javax.swing.*;
public class MySwingAppAssertJTest {
@Test
public void testButtonClick() {
FrameFixture frame = new FrameFixture(new MySwingApp().getMainFrame());
frame.button("myButton").click();
frame.label("resultLabel").requireText("Button Clicked");
}
}
3.4 高级用法
3.4.1 自定义断言
AssertJ Swing允许开发人员编写自定义的断言,以适应特定的测试需求。以下是一个简单的自定义断言示例:
import org.assertj.swing.fixture.JLabelFixture;
import org.assertj.swing.fixture.JPanelFixture;
import org.junit.Test;
import javax.swing.*;
public class MySwingAppAssertJTest {
@Test
public void testCustomAssertion() {
FrameFixture frame = new FrameFixture(new MySwingApp().getMainFrame());
JPanelFixture panel = frame.panel("myPanel");
JLabelFixture label = panel.label("customLabel");
label.requireText("Custom Text");
label.requireCustomCondition((l) -> l.text().startsWith("Custom"));
}
}
3.4.2 扩展与整合
AssertJ Swing可以与其他测试框架整合,例如JUnit。以下是一个简单的JUnit整合示例:
import org.assertj.swing.fixture.FrameFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.swing.*;
public class MySwingAppJUnitIntegrationTest {
private FrameFixture frame;
@Before
public void setUp() {
frame = new FrameFixture(new MySwingApp().getMainFrame());
frame.show();
}
@After
public void tearDown() {
frame.cleanUp();
}
@Test
public void testButtonClick() {
frame.button("myButton").click();
frame.label("resultLabel").requireText("Button Clicked");
}
}
3.5 应用示例与案例讲解
在实际项目中,AssertJ Swing的清晰断言语法可以简化测试用例的编写,并提供更好的可读性和维护性。以下是一个基于AssertJ Swing的完整测试用例:
import org.assertj.swing.fixture.FrameFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.swing.*;
public class MySwingAppFullTest {
private FrameFixture frame;
@Before
public void setUp() {
frame = new FrameFixture(new MySwingApp().getMainFrame());
frame.show();
}
@After
public void tearDown() {
frame.cleanUp();
}
@Test
public void testFullApplicationFlow() {
// Simulate user interactions
frame.textBox("inputTextBox").setText("Test Input");
frame.button("processButton").click();
// Verify the application state
frame.label("outputLabel").requireText("Processed: Test Input");
}
}
在这个测试用例中,我们使用了AssertJ Swing来模拟用户与Swing应用程序进行交互,并使用清晰的断言语法验证应用程序的状态。这种方式使测试用例易于编写和理解,提高了测试的可读性和可维护性。
4. SikuliX (图像识别与GUI自动化库)
4.1 功能概览
SikuliX是一款基于图像识别的GUI自动化库,它可以通过识别屏幕上的图像来模拟用户的操作。这使得SikuliX非常适用于那些无法通过传统GUI测试库操作的应用程序,例如游戏或具有复杂图形界面的应用程序。
4.2 安装与配置
要使用SikuliX,首先需要下载并安装SikuliX的运行环境。随后,通过引入SikuliX的Java API,可以在Java项目中使用SikuliX。
<dependency>
<groupId>com.sikulix</groupId>
<artifactId>sikulixapi</artifactId>
<version>2.0.5</version>
</dependency>
4.3 图像识别与匹配
4.3.1 区域定义与捕捉
SikuliX允许定义屏幕上的区域,并通过图像进行匹配。以下是一个简单的图像捕捉示例:
import org.sikuli.script.*;
public class SikuliXExample {
public static void main(String[] args) throws FindFailed {
Screen screen = new Screen();
Pattern imagePattern = new Pattern("path/to/image.png");
// 定义捕捉区域
Region region = new Region(100, 100, 200, 200);
// 在区域内查找图像
Match match = region.find(imagePattern);
// 在匹配到的位置进行点击
match.click();
}
}
4.3.2 图像相似性判定
SikuliX允许通过设置相似性阈值来进行图像相似性判定。以下是一个简单的相似性判定示例:
import org.sikuli.script.*;
public class SikuliXExample {
public static void main(String[] args) throws FindFailed {
Screen screen = new Screen();
Pattern imagePattern = new Pattern("path/to/image.png").similar(0.7);
// 在整个屏幕上查找相似图像
Match match = screen.find(imagePattern);
// 在匹配到的位置进行点击
match.click();
}
}
4.4 GUI交互与自动化
4.4.1 鼠标与键盘操作
SikuliX允许通过模拟鼠标和键盘操作来进行GUI自动化。以下是一个简单的鼠标点击和键盘输入示例:
import org.sikuli.script.*;
public class SikuliXExample {
public static void main(String[] args) throws FindFailed {
Screen screen = new Screen();
Pattern imagePattern = new Pattern("path/to/image.png");
// 鼠标点击图像
screen.click(imagePattern);
// 键盘输入文本
screen.type("Hello, SikuliX!");
}
}
4.4.2 界面元素识别
SikuliX允许通过图像识别来定位并操作界面元素。以下是一个简单的元素识别和操作示例:
import org.sikuli.script.*;
public class SikuliXExample {
public static void main(String[] args) throws FindFailed {
Screen screen = new Screen();
Pattern buttonPattern = new Pattern("path/to/button.png");
Pattern labelPattern = new Pattern("path/to/label.png");
// 在屏幕上查找按钮并点击
screen.click(buttonPattern);
// 在屏幕上查找标签并获取文本
String labelText = screen.find(labelPattern).text();
System.out.println("Label Text: " + labelText);
}
}
4.5 实际应用场景
SikuliX在处理图形化界面的自动化任务时具有独特的优势,特别适用于游戏、图形设计工具等无法通过传统控件定位的应用程序。在这些场景下,SikuliX的图像识别和模拟操作能够提供稳定且可靠的自动化解决方案。
5. Abbot (Java GUI测试库)
5.1 概述与用途
Abbot是一个用于Java GUI测试的库,它提供了丰富的API和工具,支持记录和回放GUI事件,以及对Swing和AWT组件进行测试。
5.2 安装与集成
要使用Abbot,首先需要在项目中添加相应的依赖。以下是使用Maven的示例:
<dependency>
<groupId>abbot</groupId>
<artifactId>abbot</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
5.3 GUI测试流程
5.3.1 GUI事件的记录与回放
Abbot允许开发人员记录GUI事件,并支持回放这些事件以进行测试。以下是一个简单的事件记录和回放示例:
import abbot.tester.ComponentTester;
import javax.swing.*;
public class AbbotExample {
public static void main(String[] args) {
JFrame frame = new JFrame("Abbot Example");
JButton button = new JButton("Click Me");
frame.getContentPane().add(button);
frame.setSize(300, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
// 创建Abbot的组件测试器
ComponentTester tester = new ComponentTester();
// 记录按钮点击事件
tester.actionClick(button);
// 回放事件
tester.actionPerform(button);
}
}
5.3.2 组件级别的断言
Abbot提供了多种断言方法,用于验证GUI组件的状态。以下是一个简单的组件状态断言示例:
import abbot.tester.ComponentTester;
import javax.swing.*;
public class AbbotExample {
public static void main(String[] args) {
JFrame frame = new JFrame("Abbot Example");
JButton button = new JButton("Click Me");
frame.getContentPane().add(button);
frame.setSize(300, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
// 创建Abbot的组件测试器
ComponentTester tester = new ComponentTester();
// 在按钮上进行断言
tester.assertText(button, "Click Me");
tester.assertEnabled(button);
tester.assertVisible(button);
}
}
5.4 高级特性
5.4.1 跨平台支持
Abbot具有跨平台支持,可以在不同操作系统上运行。以下是一个简单的跨平台测试示例:
import abbot.tester.ComponentTester;
import javax.swing.*;
public class AbbotCrossPlatformExample {
public static void main(String[] args) {
JFrame frame = new JFrame("Abbot Cross-Platform Example");
JButton button = new JButton("Click Me");
frame.getContentPane().add(button);
frame.setSize(300, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
// 创建Abbot的组件测试器
ComponentTester tester = new ComponentTester();
// 在不同操作系统上进行断言
tester.assertText(button, "Click Me");
tester.assertEnabled(button);
tester.assertVisible(button);
}
}
5.4.2 多语言测试
Abbot支持多语言测试,可以验证应用程序在不同语言环境下的正确性。以下是一个简单的多语言测试示例:
import abbot.tester.ComponentTester;
import javax.swing.*;
import java.util.Locale;
public class AbbotMultilingualExample {
public static void main(String[] args) {
// 设置应用程序的语言环境
Locale.setDefault(Locale.FRENCH);
JFrame frame = new JFrame("Abbot Multilingual Example");
JButton button = new JButton("Click Me");
frame.getContentPane().add(button);
frame.setSize(300, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
// 创建Abbot的组件测试器
ComponentTester tester = new ComponentTester();
// 在不同语言环境下进行断言
tester.assertText(button, "Cliquez sur moi");
tester.assertEnabled(button);
tester.assertVisible(button);
}
}
5.5 实际案例与项目应用
Abbot在实际项目中可以用于编写自动化GUI测试用例,确保应用程序在不同场景下的稳定性和正确性。通过结合JUnit等测试框架,可以建立全面的测试套件,涵盖应用程序的各个模块。
6. Java Robot Class (Java自动化操作库)
6.1 简介与应用场景
Java的Robot类提供了一种基于代码的方式来模拟鼠标和键盘输入,用于实现GUI自动化操作。这在一些特殊情况下,例如没有GUI测试库支持的环境中,或者需要进行屏幕级别的操作时,非常有用。
6.2 初始化与基本操作
6.2.1 创建Robot对象
Java的Robot类需要与GraphicsDevice关联,以便定位和模拟操作。以下是一个简单的Robot对象初始化示例:
import java.awt.*;
public class RobotExample {
public static void main(String[] args) throws AWTException {
// 获取默认的屏幕设备
GraphicsDevice screenDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
// 创建Robot对象
Robot robot = new Robot(screenDevice);
}
}
6.2.2 鼠标与键盘操作
Java的Robot类允许模拟鼠标和键盘的操作。以下是一个简单的鼠标点击和键盘输入示例:
import java.awt.*;
import java.awt.event.KeyEvent;
public class RobotExample {
public static void main(String[] args) throws AWTException {
Robot robot = new Robot();
// 模拟鼠标点击
robot.mouseMove(100, 100);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
// 模拟键盘输入
robot.keyPress(KeyEvent.VK_H);
robot.keyPress(KeyEvent.VK_E);
robot.keyPress(KeyEvent.VK_L);
robot.keyPress(KeyEvent.VK_L);
robot.keyPress(KeyEvent.VK_O);
}
}
6.3 高级功能
6.3.1 多屏幕支持
Java的Robot类支持多屏幕环境下的操作。以下是一个简单的多屏幕操作示例:
import java.awt.*;
import java.awt.event.InputEvent;
public class RobotMultiScreenExample {
public static void main(String[] args) throws AWTException {
// 获取所有屏幕设备
GraphicsDevice[] screenDevices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
for (GraphicsDevice screenDevice : screenDevices) {
// 创建与每个屏幕关联的Robot对象
Robot robot = new Robot(screenDevice);
// 模拟鼠标点击
robot.mouseMove(100, 100);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
}
}
}
6.3.2 剪贴板操作
Java的Robot类可以用于进行剪贴板操作,包括复制和粘贴。以下是一个简单的剪贴板操作示例:
import java.awt.*;
import java.awt.datatransfer.*;
import java.io.IOException;
public class RobotClipboardExample {
public static void main(String[] args) throws AWTException {
Robot robot = new Robot();
// 模拟复制操作
String textToCopy = "Hello, Robot Clipboard!";
StringSelection stringSelection = new StringSelection(textToCopy);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null);
// 模拟粘贴操作
robot.keyPress(KeyEvent.VK_CONTROL);
robot.keyPress(KeyEvent.VK_V);
robot.keyRelease(KeyEvent.VK_V);
robot.keyRelease(KeyEvent.VK_CONTROL);
}
}
6.4 实际应用场景
Java的Robot类在一些场景下是一种强大而灵活的工具,可以用于处理一些GUI自动化无法覆盖的情况。在某些特定需求下,例如自动化测试一些系统级别的操作、模拟用户输入等,Java的Robot类是一个可行的选择。
总结
通过本文的介绍,读者对于Java GUI测试与自动化的解决方案有了更全面的了解。不同的库适用于不同的场景,例如TestFX适用于JavaFX应用程序测试,SikuliX适用于图像识别,而Abbot则提供了丰富的API和工具支持。读者可以根据项目需求选择合适的库,提高测试效率,确保应用程序的质量。
原文地址:https://blog.csdn.net/qq_42531954/article/details/136019918
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_66731.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!