zoukankan      html  css  js  c++  java
  • testng优化:失败重跑,extentReport+appium用例失败截图,测试报告发邮件

    生成的单html方便jenkins集成发邮件,= = 构建失败发邮件

    参考:https://blog.csdn.net/galen2016/article/details/77975965

    步骤:

      1.安装Email Extension Plugin插件

      2.系统管理--系统设置

        --设置jenkins Location的邮箱,这里是管理员邮箱,要和发件人的邮箱设置为同一个

     

        --设置email extension plugin

        --设置邮件通知,可以测试一下收发件

      2.进入项目-设置-构建后操作

      

        --高级设置

        --Default Content内容

    <!DOCTYPE html>  
    <html>  
    <head>  
    <meta charset="UTF-8">  
    <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>  
    </head>  
    
    <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"  
        offset="0">  
        <div>
        <table width="95%" cellpadding="0" cellspacing="0" 
            style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif"> 
    
            <tr>
                <th align="center" colspan="2"><br />
                    <h2>构建信息</h2> 
                </th>
            </tr>
            <tr>  
                <td>  
                    <ul>  
                        <li>项目名称 : ${PROJECT_NAME}</li><br />  
                        <li>详细测试报告 : <a href="${PROJECT_URL}测试报告">${PROJECT_URL}测试报告</a></li><br />
                        <li>触发原因: ${CAUSE}</li><br />                    
                        <li>项目  Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li><br />
                    </ul>  
                </td> 
                <td>${JELLY_SCRIPT,template="html"}<br/> 
            </tr>  
    
            <tr>  
                <td colspan="2" align="center"><br />
                <h2>测试报告</h2>
                </td>  
            </tr>
    
            <tr>
                <td colspan="2" align="center">
                    <div>${FILE ,path="C:/Users/user/workspace/testngDemo/test-output/emailable-report.html"}</div>
                </td>
            </tr>
    
        </table> 
        </div>
    
      </body>  
    </html>
    

      

        
     

    失败重跑 testng实现 √

     https://blog.csdn.net/u011541946/article/details/78688302

    有两种实现方法:

    1.用runtestng直接java执行

    2.用testng的监听

    Retry设定重跑次数

    public class Retry implements IRetryAnalyzer {
    	private int retryCount =0;
    	private int maxRetryCount =2;
    	@Override
    	public boolean retry(ITestResult result){
    		if(retryCount<maxRetryCount){
    			retryCount++;
    			return true;
    		}
    		return false;
    	}
    }  

    重跑的监听器

    public class RetryTestListener implements IAnnotationTransformer {
        public void transform(ITestAnnotation annotation, Class testClass,
                Constructor testConstructor, Method testMethod) {
            IRetryAnalyzer retry = annotation.getRetryAnalyzer();
            if (retry == null) {
                annotation.setRetryAnalyzer(Retry.class);
            }
        }
    }
    

    设置用例结束的监听器

    public class ListenerTest extends TestListenerAdapter{
    	  @Override
    	  public void onFinish(ITestContext testContext){
    		  super.onFinish(testContext);
    		  Iterator<ITestResult> listOfFailedTests=testContext.getFailedTests().getAllResults().iterator();
    		  while(listOfFailedTests.hasNext()){
    			  ITestResult failedTest = listOfFailedTests.next();
    			  ITestNGMethod method = failedTest.getMethod();
    			  if(testContext.getFailedTests().getResults(method).size()>1){
    				  listOfFailedTests.remove();
    			  }else{
    				  if(testContext.getPassedTests().getResults(method).size()>0){
    					  listOfFailedTests.remove();
    				  }
    			  }
    		  }
    	  }
    }
    

     

    在testng.xml中配置监听器

    <listeners >
            <listener class-name="b.testng.reportRelated.ListenerTest"/> 
        	<listener class-name="com.test.RetryTestListener"/>
    </listeners>

     

    失败用例截图√

    截图方法:

    public class ScreenScr {
    	public static void getScreen(TakesScreenshot driver ,String picName){
    		String filePath=System.getProperty("user.dir");
            File pic=driver.getScreenshotAs(OutputType.FILE);
    //        String picName=System.currentTimeMillis()+".png";
            try {
                FileUtils.copyFile(pic, new File(filePath+"\pic\"+picName+".png"));
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                Reporter.log("GetScreenshot Successful"+filePath+"\pic\"+picName);
            }
    	}
    }

    失败和截图结合起来,把截图方法放进TestListenerAdapter的继承类里面

    public class ListenerTest extends TestListenerAdapter{
    	private Logger logger = Logger.getLogger(ListenerTest.class);
    	private int m_count = 0;
    	String filePath=System.getProperty("user.dir");
              @Override
    	  public void onTestFailure(ITestResult tr) {
    		  super.onTestFailure(tr);
      		String picName =tr.getStartMillis()+"";
      		ScreenScr.getScreen(PhoneDriver.getPhoneDriver(), picName);
    	    log("["+tr.getName()+" Failure ]"+"F");
    	    Reporter.log("["+tr.getName()+"失败]");
    	  }
    	 
    	  @Override
    	  public void onTestSkipped(ITestResult tr) {
    		  super.onTestSkipped(tr);
    	  		String picName =tr.getStartMillis()+"";
    	  		ScreenScr.getScreen(PhoneDriver.getPhoneDriver(), picName);
    	    log("["+tr.getName()+" skipped ]"+"S");
    	    Reporter.log("["+tr.getName()+"跳过]");
    	  }
    }    
    

      最后一步,把失败截图放进报告,这里我用的extentReporter,继承了IReporter

      这里截取一段用到截图的代码,是extentReporter在报告中,对用例结果的显示的代码,其他项目可以参考。

    if (result.getThrowable() != null) {
                    	try {
                    		String fileName=System.getProperty("user.dir")+"\pic\"+result.getStartMillis()+".png";
    						test.addScreenCaptureFromPath(fileName);//本地可看,klov端裂图,klov的bug
    						//本地和klov都可看
    						test.fail("截图", MediaEntityBuilder.createScreenCaptureFromPath(fileName).build());
    					} catch (IOException e) {
    						e.printStackTrace();
    					}
                        test.log(status, result.getThrowable());
                    }else {
                        test.log(status, "Test " + status.toString().toLowerCase() + "ed");
                    }
    

    用例执行失败发测试报告邮件

    参考:https://www.cnblogs.com/h--d/p/6138900.html

    https://www.cnblogs.com/ysocean/p/7666061.html

    步骤

      1.写一个发邮件的类Mail,这里把测试报告的html放在附件里面发送了。也可以放在content里面。

    package b.testng.reportRelated;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.Properties;
    
    import javax.activation.DataHandler;
    import javax.activation.DataSource;
    import javax.activation.FileDataSource;
    import javax.mail.BodyPart;
    import javax.mail.Message;
    import javax.mail.MessagingException;
    import javax.mail.Session;
    import javax.mail.Transport;
    import javax.mail.internet.AddressException;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeBodyPart;
    import javax.mail.internet.MimeMessage;
    import javax.mail.internet.MimeMultipart;
    import javax.mail.internet.MimeUtility;
    
    public class Email {
    	// supply your SMTP host name
    	private static String host = "smtp.163.com";
    	// supply a static sendTo list
    	private static String to = "350xxxxx@qq.com";
    	// supply the sender email
    	private static String from = "yxxxxx@163.com";
    
    	private static String password = "Yxxxxxx";
    
    	/*
    	 * // default cc list 抄送 private static String cc = ""; // default bcc list
    	 * 密送 private static String bcc = "";
    	 */
    
    	private static String htmlToContents(String htmlPath, String htmlName) {
    		String string = "";
    		FileReader in;
    		try {
    			in = new FileReader(htmlPath + htmlName);
    
    			char[] buff = new char[1024 * 10];
    			in.read(buff);
    
    			string = new String(buff);
    			in.close();
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		return string;
    	}
    
    	private static void sendEmail(String from, String to, String subject, String content, String htmlPath,
    			String htmlName,String attachPath, String attachName) {
    		try {
    			// 创建参数配置,用于连接邮件服务器
    			Properties prop = System.getProperties();
    			prop.setProperty("mail.transport.protocol", "smtp");
    			prop.setProperty("mail.smtp.host", host);
    			prop.setProperty("mail.smtp.auth", "true");
    			// 创建会话,和邮件服务器交互
    			Session session = Session.getDefaultInstance(prop);
    			session.setDebug(true);
    			// 创建一封邮件,设置session,发送者,接收者,标题
    			MimeMessage message = new MimeMessage(session);
    			message.setFrom(new InternetAddress(from));
    			message.setSubject(subject);
    			List<String> toList = getAddress(to);
    			for (String address : toList) {
    				message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));
    			}
    			String contents="";
    			
    			if (!htmlPath.equals("")) {
    				contents =htmlToContents(htmlPath, htmlName);
    			}
    			contents=content+contents;
    			if (!attachPath.equals("")) {
    				// 整封邮件的MIME消息体
    				MimeMultipart msgMultipart = new MimeMultipart("mixed");
    				// ↑有附件,没有附件会报空指针
    				message.setContent(msgMultipart);
    				// 往MIME消息体中,加入附件
    				MimeBodyPart attch = new MimeBodyPart();
    				msgMultipart.addBodyPart(attch);
    				// 把文件添加到attch中
    				// 设置数据源
    				DataSource ds = new FileDataSource(new File(attachPath + attachName));
    				// 设置数据处理器
    				DataHandler dHandler = new DataHandler(ds);
    				// 设置第一个附件的数据
    				attch.setDataHandler(dHandler);
    				// 设置第一个附件的文件名
    				attch.setFileName(attachName);
    				// 正文
    				// 往MIME消息体中,加入文字
    				MimeBodyPart text = new MimeBodyPart();
    				text.setContent(contents, "text/html;charset=UTF-8");
    				message.saveChanges();
    			}
    			else {
    				
    				message.setContent(contents, "text/html;charset=UTF-8");
    			}
    			// 根据session获取邮件传输对象
    			Transport transport = session.getTransport();
    			transport.connect(from, password);
    			transport.sendMessage(message, message.getAllRecipients());
    			transport.close();
    		} catch (MessagingException e) {
    			e.printStackTrace();
    		}
    	}
    
    	/**
    	 * send 2个参数 使用默认邮箱,发送subject和content
    	 */
    	public static void send(String subject, String contents) {
    		sendEmail(from, to, subject, contents,  "", "", "", "");
    	}
    
    	/**
    	 * send 3个参数 发送 指定邮箱,不发到默认邮箱,subject和content
    	 */
    	public static void send(String to, String subject, String contents) {
    		sendEmail(from, to, subject, contents, "", "", "", "");
    	}
    
    	/**
    	 * sendAttach 4个参数 使用默认邮箱,发送subject和content,attch
    	 */
    	public static void sendAttach(String subject, String contents, String attachPath, String attachName) {
    		sendEmail(from, to, subject, contents, "", "", attachPath, attachName);
    	}
    
    	/**
    	 * sendAttach 3个参数 使用默认邮箱,发送subject和content,attch
    	 */
    	public static void sendAttach(String to, String subject, String contents, String attachPath, String attachName) {
    		sendEmail(from, to, subject, contents,  "", "", attachPath, attachName);
    	}
    
    	private void sendWithPic(String picPath,String picName,String picURL) throws AddressException, MessagingException{
    		Properties prop = System.getProperties();
    		prop.setProperty("mail.transport.protocol", "smtp");
    		prop.setProperty("mail.smtp.host", host);
    		prop.setProperty("mail.smtp.auth", "true");
    		// 创建会话,和邮件服务器交互
    		Session session = Session.getDefaultInstance(prop);
    		session.setDebug(true);
    		// 创建一封邮件,设置session,发送者,接收者,标题
    		MimeMessage message = new MimeMessage(session);
    		message.setFrom(new InternetAddress(from));
    		message.setSubject("subject");
    		List<String> toList = getAddress(to);
    		for (String address : toList) {
    			message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));
    		}
            
           // 5. 创建图片"节点"
           MimeBodyPart image = new MimeBodyPart();
           // 读取本地文件
           DataHandler dh = new DataHandler(new FileDataSource(picPath+picName));
           // 将图片数据添加到"节点"
           image.setDataHandler(dh);
           // 为"节点"设置一个唯一编号(在文本"节点"将引用该ID)
           image.setContentID(picName);    
            
           // 6. 创建文本"节点"
           MimeBodyPart text = new MimeBodyPart();
           // 这里添加图片的方式是将整个图片包含到邮件内容中, 实际上也可以以 http 链接的形式添加网络图片
           text.setContent("这是一张图片<br/><a href='"+picURL+"'><img src='cid:"+picName+"'/></a>", "text/html;charset=UTF-8");
            
           // 7. (文本+图片)设置 文本 和 图片"节点"的关系(将 文本 和 图片"节点"合成一个混合"节点")
           MimeMultipart mm_text_image = new MimeMultipart();
           mm_text_image.addBodyPart(text);
           mm_text_image.addBodyPart(image);
           mm_text_image.setSubType("related");    // 关联关系
            
           // 8. 将 文本+图片 的混合"节点"封装成一个普通"节点"
           // 最终添加到邮件的 Content 是由多个 BodyPart 组成的 Multipart, 所以我们需要的是 BodyPart,
           // 上面的 mailTestPic 并非 BodyPart, 所有要把 mm_text_image 封装成一个 BodyPart
           MimeBodyPart text_image = new MimeBodyPart();
           text_image.setContent(mm_text_image);
    	        //设置邮件的发送时间,默认立即发送
    	        message.setSentDate(new Date());
    	}
    
    	/**
    	 * 发送标题,内容,html
    	 * 
    	 * @param subject
    	 * @param contents
    	 * @param htmlPath
    	 * @param htmlName
    	 */
    	public static void sendHtml(String subject, String contents, String htmlPath, String htmlName) {
    		sendEmail(from, to, subject, contents, htmlPath, htmlName, "", "");
    	}
    
    	/**
    	 * 发送标题,html
    	 * 
    	 * @param subject
    	 * @param htmlPath
    	 * @param htmlName
    	 */
    	public static void sendHtml(String subject, String htmlPath, String htmlName) {
    		sendEmail(from, to, subject, "", htmlPath, htmlName, "", "");
    	}
    
    	private static List<String> getAddress(String address) {
    		List<String> addressList = new ArrayList<String>();
    		if (address.isEmpty())
    			return addressList;
    
    		if (address.indexOf(";") > 0) {
    			String[] addresses = address.split(";");
    
    			for (String a : addresses) {
    				addressList.add(a);
    			}
    		} else {
    			addressList.add(address);
    		}
    
    		return addressList;
    	}
    
    }
    

      

      2.在maven配置中,增加一个插件,该插件可以在测试失败后执行配置的class的main方法

    参考https://blog.csdn.net/qq744746842/article/details/51497506

    			<plugin>
      				<groupId>org.codehaus.mojo</groupId>
      				  <artifactId>exec-maven-plugin</artifactId>
      				  <version>1.1.1</version>
       				 <executions>
        				    <execution>
         				      <phase>test</phase>
          				      <goals>
            				        <goal>java</goal>
             				  </goals>
            				  <configuration>
             				       <includePluginDependencies>true</includePluginDependencies>
             				       <mainClass>b.testng.reportRelated.Email</mainClass>
                				   <arguments>
                  				      <argument>11</argument>
                 				      <argument>22</argument>
                				   </arguments>
             				  </configuration>
           				 	</execution>
        				</executions>
      				</plugin>
    

      

    package b.testng.reportRelated;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.Properties;
    import javax.activation.DataHandler;
    import javax.activation.DataSource;
    import javax.activation.FileDataSource;
    import javax.mail.BodyPart;
    import javax.mail.Message;
    import javax.mail.MessagingException;
    import javax.mail.Session;
    import javax.mail.Transport;
    import javax.mail.internet.AddressException;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeBodyPart;
    import javax.mail.internet.MimeMessage;
    import javax.mail.internet.MimeMultipart;
    import javax.mail.internet.MimeUtility;
    
    public class Email {
    	// supply your SMTP host name
    	private static String host = "smtp.163.com";
    	// supply a static sendTo list
    	private static String to = "350343***@qq.com";
    	// supply the sender email
    	private static String from = "yindongzi@163.com";
    	private static String password = "Yzz1990";
    
    	/*
    	 * * // default cc list 抄送 private static String cc = ""; // default bcc
    	 * list * 密送 private static String bcc = "";
    	 */
    	private static String htmlToContents(String htmlPath, String htmlName) {
    		String string = "";
    		FileReader in;
    		try {
    			in = new FileReader(htmlPath + htmlName);
    			char[] buff = new char[1024 * 10];
    			in.read(buff);
    			string = new String(buff);
    			in.close();
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		return string;
    	}
    
    private static void sendEmail(String from, String to, String subject, String content, String htmlPath,String htmlName,String attachPath, String attachName) {try {// 创建参数配置,用于连接邮件服务器Properties prop = System.getProperties();prop.setProperty("mail.transport.protocol", "smtp");prop.setProperty("mail.smtp.host", host);prop.setProperty("mail.smtp.auth", "true");// 创建会话,和邮件服务器交互Session session = Session.getDefaultInstance(prop);session.setDebug(true);// 创建一封邮件,设置session,发送者,接收者,标题MimeMessage message = new MimeMessage(session);message.setFrom(new InternetAddress(from));message.setSubject(subject);List<String> toList = getAddress(to);for (String address : toList) {message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));}String contents="";if (!htmlPath.equals("")) {contents =htmlToContents(htmlPath, htmlName);}contents=content+contents;if (!attachPath.equals("")) {// 整封邮件的MIME消息体MimeMultipart msgMultipart = new MimeMultipart("mixed");// ↑有附件,没有附件会报空指针message.setContent(msgMultipart);// 往MIME消息体中,加入附件MimeBodyPart attch = new MimeBodyPart();msgMultipart.addBodyPart(attch);// 把文件添加到attch中// 设置数据源DataSource ds = new FileDataSource(new File(attachPath + attachName));// 设置数据处理器DataHandler dHandler = new DataHandler(ds);// 设置第一个附件的数据attch.setDataHandler(dHandler);// 设置第一个附件的文件名attch.setFileName(attachName);// 正文// 往MIME消息体中,加入文字MimeBodyPart text = new MimeBodyPart();text.setContent(contents, "text/html;charset=UTF-8");message.saveChanges();}else {message.setContent(contents, "text/html;charset=UTF-8");}// 根据session获取邮件传输对象Transport transport = session.getTransport();transport.connect(from, password);transport.sendMessage(message, message.getAllRecipients());transport.close();} catch (MessagingException e) {e.printStackTrace();}}
    
    	/** * send 2个参数 使用默认邮箱,发送subject和content */
    	public static void send(String subject, String contents) {
    		sendEmail(from, to, subject, contents, "", "", "", "");
    	}
    
    	/** * send 3个参数 发送 指定邮箱,不发到默认邮箱,subject和content */
    	public static void send(String to, String subject, String contents) {
    		sendEmail(from, to, subject, contents, "", "", "", "");
    	}
    
    	/** * sendAttach 4个参数 使用默认邮箱,发送subject和content,attch */
    	public static void sendAttach(String subject, String contents, String attachPath, String attachName) {
    		sendEmail(from, to, subject, contents, "", "", attachPath, attachName);
    	}
    
    	/** * sendAttach 3个参数 使用默认邮箱,发送subject和content,attch */
    	public static void sendAttach(String to, String subject, String contents, String attachPath, String attachName) {
    		sendEmail(from, to, subject, contents, "", "", attachPath, attachName);
    	}
    
    private void sendWithPic(String picPath,String picName,String picURL) throws AddressException, MessagingException{Properties prop = System.getProperties();prop.setProperty("mail.transport.protocol", "smtp");prop.setProperty("mail.smtp.host", host);prop.setProperty("mail.smtp.auth", "true");// 创建会话,和邮件服务器交互Session session = Session.getDefaultInstance(prop);session.setDebug(true);// 创建一封邮件,设置session,发送者,接收者,标题MimeMessage message = new MimeMessage(session);message.setFrom(new InternetAddress(from));message.setSubject("subject");List<String> toList = getAddress(to);for (String address : toList) {message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));}               // 5. 创建图片"节点"       MimeBodyPart image = new MimeBodyPart();       // 读取本地文件       DataHandler dh = new DataHandler(new FileDataSource(picPath+picName));       // 将图片数据添加到"节点"       image.setDataHandler(dh);       // 为"节点"设置一个唯一编号(在文本"节点"将引用该ID)       image.setContentID(picName);                   // 6. 创建文本"节点"       MimeBodyPart text = new MimeBodyPart();       // 这里添加图片的方式是将整个图片包含到邮件内容中, 实际上也可以以 http 链接的形式添加网络图片       text.setContent("这是一张图片<br/><a href='"+picURL+"'><img src='cid:"+picName+"'/></a>", "text/html;charset=UTF-8");               // 7. (文本+图片)设置 文本 和 图片"节点"的关系(将 文本 和 图片"节点"合成一个混合"节点")       MimeMultipart mm_text_image = new MimeMultipart();       mm_text_image.addBodyPart(text);       mm_text_image.addBodyPart(image);       mm_text_image.setSubType("related");    // 关联关系               
    // 8. 将 文本+图片 的混合"节点"封装成一个普通"节点"       // 最终添加到邮件的 Content 是由多个 BodyPart 组成的 Multipart, 所以我们需要的是 BodyPart,       // 上面的 mailTestPic 并非 BodyPart, 所有要把 mm_text_image 封装成一个 BodyPart       MimeBodyPart text_image = new MimeBodyPart();       text_image.setContent(mm_text_image);        //设置邮件的发送时间,默认立即发送        message.setSentDate(new Date());}
    
    	/**
    	 * * 发送标题,内容,html * * @param subject * @param contents * @param htmlPath
    	 * * @param htmlName
    	 */
    	public static void sendHtml(String subject, String contents, String htmlPath, String htmlName) {
    		sendEmail(from, to, subject, contents, htmlPath, htmlName, "", "");
    	}
    
    	/** * 发送标题,html * * @param subject * @param htmlPath * @param htmlName */
    	public static void sendHtml(String subject, String htmlPath, String htmlName) {
    		sendEmail(from, to, subject, "", htmlPath, htmlName, "", "");
    	}
    
    	private static List<String> getAddress(String address) {
    		List<String> addressList = new ArrayList<String>();
    		if (address.isEmpty())
    			return addressList;
    		if (address.indexOf(";") > 0) {
    			String[] addresses = address.split(";");
    			for (String a : addresses) {
    				addressList.add(a);
    			}
    		} else {
    			addressList.add(address);
    		}
    		return addressList;
    	}
    }
    

      

  • 相关阅读:
    【转】 robotframework(rf)中对时间操作的datetime库常用关键字
    在RobotFramework--RIDE中把日期转化为整型进行运算
    Oracle中date转为timstam可以函数to_timestamp的方式来转化
    Java项目缺少.project文件
    数据库时间戳转换日期(MYSQL数据库)
    spring+struts+mybatis中关于报错org.hibernate.exception.GenericJDBCException: Connection is read-only. Queries leading to data modification are not allowed 的产生原因及解决方案
    新加字段问题(增加联合主键)
    集合问题
    数组面试题
    集合的问题
  • 原文地址:https://www.cnblogs.com/zhizhiyin/p/9068631.html
Copyright © 2011-2022 走看看