// Copyright 2010, Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.api.adwords.lib.utils;
import com.google.api.adwords.lib.AdWordsService;
import com.google.api.adwords.lib.AdWordsUser;
import com.google.api.adwords.lib.ReportDate;
import com.google.api.adwords.v13.ApiException;
import com.google.api.adwords.v13.DefinedReportJob;
import com.google.api.adwords.v13.ReportInterface;
import junit.framework.TestCase;
import org.xml.sax.SAXException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.rmi.RemoteException;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.rpc.ServiceException;
/**
* Tests the {@link ReportUtils} utility class. For all tests, the credentials
* for this test will be pulled from "test_data/test.properties" and these
* credentials should target a sandbox client such as "client_1+<i>email</i>"
* in the clientEmail header. These tests will only test the data retrieved in
* the sandbox environment. This test takes a very long time to run
* (~10-15 minutes).
*
* @author api.arogal@gmail.com (Adam Rogal)
*/
public class ReportUtilsTest extends TestCase {
/** The charset for the report XML. */
private static final Charset REPORT_XML_CHARSET = Charset.forName("UTF-8");
private final int OBJECT_DOES_NOT_EXIST_CODE = 5;
private AdWordsUser testUser;
private ReportInterface reportService;
private DefinedReportJob reportJob;
private long finishedReportJobId;
private ReportUtils testReportUtils;
private String expectedXml;
private ReportCallback reportCallback;
private boolean asyncFailure = false;
private boolean asyncInterrupted = false;
private Thread asyncThread;
private Exception asyncException;
private String nonAsciiReportXml;
private List<String[]> nonAsciiReportCsv;
@Override
protected void setUp() throws Exception {
super.setUp();
testUser = new AdWordsUser("test_data/test.properties");
reportService = testUser.getService(AdWordsService.V13.REPORT_SERVICE);
reportJob = new DefinedReportJob();
reportJob.setSelectedReportType("Structure");
reportJob.setAggregationTypes(new String[] {"Keyword"});
reportJob.setStartDay(new ReportDate(2009, 0, 1).toDate());
reportJob.setEndDay(new ReportDate(2009, 0, 31).toDate());
reportJob.setName("Demo Structure Keyword Report");
reportJob.setSelectedColumns(new String[] {
"Campaign", "CampaignId", "AdGroup", "AdGroupId", "Keyword",
"KeywordId", "MaximumCPC"});
expectedXml = loadFile("test_data/expected.xml");
new File("test_data/output.xml.gz").delete();
new File("test_data/output.xml").delete();
new File("test_data/output.jpg").delete();
nonAsciiReportXml = loadFile("test_data/report_non_ascii.xml");
nonAsciiReportCsv = CsvUtils.getCsvDataArray("test_data/report_non_ascii.csv", false);
reportCallback = new ReportCallback() {
public void onSuccess() {
synchronized (reportCallback) {
reportCallback.notify();
}
}
public void onException(Exception e) {
asyncException = e;
synchronized (reportCallback) {
reportCallback.notify();
}
}
public void onFailure() {
asyncFailure = true;
synchronized (reportCallback) {
reportCallback.notify();
}
}
public void onInterruption() {
asyncInterrupted = true;
synchronized (reportCallback) {
reportCallback.notify();
}
}
};
asyncException = null;
asyncFailure = false;
asyncInterrupted = false;
asyncThread = null;
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
new File("test_data/output.xml.gz").delete();
new File("test_data/output.xml").delete();
new File("test_data/output.jpg").delete();
}
/**
* Tests downloading of a nonexistent gzipped report.
*/
public void testDownloadReport_gzNonexistentReport() throws IOException {
try {
new ReportUtils(reportService, -1).downloadReport(true, "test_data/output.xml.gz");
fail("ApiException not thrown.");
} catch (ApiException e) {
assertEquals(e.getCode(), OBJECT_DOES_NOT_EXIST_CODE);
}
}
/**
* Tests downloading a successful gzipped report.
*/
public void testDownloadReport_gzSuccessful() throws IOException, InterruptedException,
ServiceException {
scheduleReportAndWait();
testReportUtils.downloadReport(true, "test_data/output.xml.gz");
assertEqualFiles("test_data/expected.xml.gz", "test_data/output.xml.gz");
}
/**
* Tests downloading a nonexistent non-gzipped report.
*/
public void testDownloadReport_nonGzNonexistent() throws IOException {
try {
new ReportUtils(reportService, -1).downloadReport(true, "test_data/output.xml.gz");
} catch (ApiException e) {
assertEquals(e.getCode(), OBJECT_DOES_NOT_EXIST_CODE);
}
}
/**
* Tests downloading a successful non-gzipped report.
*/
public void testDownloadReport_nonGzSuccessful() throws IOException, InterruptedException,
ServiceException {
scheduleReportAndWait();
testReportUtils.downloadReport(false, "test_data/output.xml");
assertEqualFiles("test_data/expected.xml", "test_data/output.xml");
}
/**
* Tests retrieving XML of a nonexistent report.
*/
public void testGetReportXml_nonexistentReport() throws IOException {
try {
new ReportUtils(reportService, -1).getReportXml();
fail("ApiException not thrown.");
} catch (ApiException e) {
assertEquals(e.getCode(), OBJECT_DOES_NOT_EXIST_CODE);
}
}
/**
* Tests retrieving XML of a successful report.
*/
public void testGetReportXml_successful() throws IOException, ServiceException,
InterruptedException {
scheduleReportAndWait();
String actual = testReportUtils.getReportXml();
assertEquals(expectedXml, actual);
}
/**
* Tests asynchronously waiting for a successful report.
*/
public void testWhenReportReady() throws RemoteException, InterruptedException {
asyncThread = new ReportUtils(reportService, reportService.scheduleReportJob(reportJob))
.whenReportReady(reportCallback);
waitForCallback();
assertFalse("Report Failure occured.", asyncFailure);
assertFalse("Asynchronous thread was interrupted.", asyncInterrupted);
assertNull("Exception occured on callback.", asyncException);
}
/**
* Tests asynchronously waiting for a failed report.
*/
public void testWhenReportReady_failure() throws RemoteException, InterruptedException {
reportJob.setName("fixstatus=Failed");
asyncThread = new ReportUtils(reportService, reportService.scheduleReportJob(reportJob))
.whenReportReady(reportCallback);
waitForCallback();
assertTrue("Report Failure was expected.", asyncFailure);
assertFalse("Asynchronous thread was interrupted.", asyncInterrupted);
assertNull("Exception occured on callback.", asyncException);
}
/**
* Tests asynchronously waiting for a report and interrupting the thread.
*/
public void testWhenReportReady_interrupt() throws RemoteException,
InterruptedException {
asyncThread = new ReportUtils(reportService, reportService.scheduleReportJob(reportJob))
.whenReportReady(reportCallback);
waitForCallbackAndInterrupt(asyncThread);
assertFalse("Report Failure occured.", asyncFailure);
assertTrue("Asynchronous thread was not interrupted.", asyncInterrupted);
assertNull("Exception occured on callback.", asyncException);
}
/**
* Tests asynchronously waiting a non existent report.
*/
public void testWhenReportReady_nonExistentReport() throws InterruptedException {
new ReportUtils(reportService, -1).whenReportReady(reportCallback);
waitForCallback();
assertNotNull("No exception thrown.", asyncException);
assertTrue("Expected exception type ApiException was " + asyncException.getClass(),
asyncException instanceof ApiException);
assertEquals("Incorrect exception code.",
((ApiException) asyncException).getCode(), OBJECT_DOES_NOT_EXIST_CODE);
}
/**
* Tests getting a CSV from an unparsable report.
*/
public void testGetCsvFromReportXml_unparsable() throws IOException,
ParserConfigurationException {
try {
ReportUtils.getReportCsv("");
fail("SAXException not thrown.");
} catch (SAXException e) {
// Ignore exception.
}
}
/**
* Tests getting a CSV from a simple XML.
*/
public void testGetCsvFromReportXml_simpleXml() throws IOException, ParserConfigurationException,
SAXException {
assertNotNull(ReportUtils.getReportCsv("<xml></xml>"));
}
/**
* Tests getting a CSV from a report.
*/
public void testGetCsvFromReportXml_reportXml() throws IOException, ParserConfigurationException,
SAXException {
CsvUtilsTest.assertDataArrayEquals(nonAsciiReportCsv, ReportUtils
.getReportCsv(nonAsciiReportXml));
}
/**
* Schedules a report and waits for it to finish.
*/
private void scheduleReportAndWait() throws IOException, ServiceException, InterruptedException {
finishedReportJobId = reportService.scheduleReportJob(reportJob);
testReportUtils = new ReportUtils(testUser, finishedReportJobId);
if(!testReportUtils.waitForReportReady()) {
fail("Report could not be scheduled");
}
}
/**
* Waits for the report callback.
*
* @throws InterruptedException if this thread was interrupted
*/
private void waitForCallback() throws InterruptedException {
synchronized (reportCallback) {
reportCallback.wait();
}
}
/**
* Interupts the callback thread while waiting.
*
* @param asyncThread the thred to interrupted
* @throws InterruptedException if this thread was interrupted
*/
private void waitForCallbackAndInterrupt(Thread asyncThread) throws InterruptedException {
synchronized (reportCallback) {
asyncThread.interrupt();
reportCallback.wait();
}
}
/**
* Returns the contents of the file in UTF-8 encoding
*
* @param file the file to load
* @return the UTF-8 encoded contents of the file
* @throws IOException if the file can't be loaded
*/
public static String loadFile(String file) throws IOException {
if (file == null) {
throw new IllegalArgumentException("File cannot be null.");
}
StringBuilder fileContents = new StringBuilder();
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(
new FileInputStream(file), REPORT_XML_CHARSET));
String line = "";
while ((line = br.readLine()) != null) {
fileContents.append(line + "\n");
}
} finally {
if (br != null) {
br.close();
}
}
return fileContents.toString();
}
/**
* Asserts that the contents of the two files are the same.
*
* @param expected the expected file
* @param actual the actual file
* @throws IOException if either file could not be loaded
*/
public static void assertEqualFiles(String expected, String actual) throws IOException {
assertEquals(loadFile(expected), loadFile(actual));
}
}