/* * The contents of this file are subject to the OpenMRS Public License * Version 1.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://license.openmrs.org * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * Copyright (C) OpenMRS, LLC. All Rights Reserved. */ package org.openmrs.module.reporting.report.renderer; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.openmrs.module.reporting.ReportingConstants; import org.openmrs.module.reporting.common.TestUtil; import org.openmrs.module.reporting.dataset.DataSet; import org.openmrs.module.reporting.dataset.DataSetColumn; import org.openmrs.module.reporting.dataset.SimpleDataSet; import org.openmrs.module.reporting.dataset.definition.SqlDataSetDefinition; import org.openmrs.module.reporting.evaluation.EvaluationContext; import org.openmrs.module.reporting.evaluation.parameter.Mapped; import org.openmrs.module.reporting.report.ReportData; import org.openmrs.module.reporting.report.ReportRequest; import org.openmrs.module.reporting.report.definition.ReportDefinition; import org.openmrs.module.reporting.report.definition.service.ReportDefinitionService; import org.openmrs.test.BaseModuleContextSensitiveTest; import org.springframework.beans.factory.annotation.Autowired; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; public class DelimitedTextReportRendererTest extends BaseModuleContextSensitiveTest { @Autowired ReportDefinitionService reportDefinitionService; @Test public void getRenderedContentType_shouldBeZipIfMoreThanOneDataSet() throws Exception { DelimitedTextReportRenderer renderer = new CsvReportRenderer(); assertThat(renderer.getRenderedContentType(requestFor(reportDefinitionWithTwoDSDs())), is("application/zip")); } @Test public void getRenderedContentType_shouldBeCsvIfOneDataSet() throws Exception { DelimitedTextReportRenderer renderer = new CsvReportRenderer(); assertThat(renderer.getRenderedContentType(requestFor(reportDefinitionWithOneDSD())), is("text/csv")); } @Test public void getFilename_shouldBeZipIfMoreThanOneDataSet() throws Exception { DelimitedTextReportRenderer renderer = new CsvReportRenderer(); assertTrue(Pattern.matches("Testing_.*\\.zip", renderer.getFilename(requestFor(reportDefinitionWithTwoDSDs())))); } @Test public void getFilename_shouldBeCsvIfOneDataSet() throws Exception { DelimitedTextReportRenderer renderer = new CsvReportRenderer(); assertTrue(Pattern.matches("Testing_.*\\.csv", renderer.getFilename(requestFor(reportDefinitionWithOneDSD())))); } @Test public void render_shouldWritePlainTextIfOneDataSet() throws Exception { DelimitedTextReportRenderer renderer = new CsvReportRenderer(); ReportData data = reportDefinitionService.evaluate(reportDefinitionWithOneDSD(), new EvaluationContext()); ByteArrayOutputStream out = new ByteArrayOutputStream(); renderer.render(data, "", out); assertThat(out.toString().toLowerCase(), startsWith("\"patient_id\"")); } @Test public void render_shouldWriteZipIfMoreThanOneDataSet() throws Exception { DelimitedTextReportRenderer renderer = new CsvReportRenderer(); ReportData data = reportDefinitionService.evaluate(reportDefinitionWithTwoDSDs(), new EvaluationContext()); ByteArrayOutputStream out = new ByteArrayOutputStream(); renderer.render(data, "", out); ZipInputStream zip = new ZipInputStream(new ByteArrayInputStream(out.toByteArray())); try { ZipEntry entry = zip.getNextEntry(); assertThat(entry.getName(), is("collision with tricky tough characters.csv")); entry = zip.getNextEntry(); assertThat(entry.getName(), is("collision with tricky tough characters_2.csv")); } finally { IOUtils.closeQuietly(zip); } // I also did the following, then manually opened the zip file, and opened the CSVs in LibreOffice. It worked // correctly. I'm commenting it out since it's pointless for automated testing. // FileOutputStream fos = new FileOutputStream("/tmp/test.zip"); // renderer.render(data, "", fos); // IOUtils.closeQuietly(fos); } @Test public void writeDataSet_shouldBeAbleToWriteUtf8() throws Exception { testWriteDataSetAs("UTF-8"); } @Test public void writeDataSet_shouldBeAbleToWriteLatin1() throws Exception { testWriteDataSetAs("ISO-8859-1"); } private void testWriteDataSetAs(String characterEncoding) throws IOException { SimpleDataSet ds = new SimpleDataSet(null, null); ds.addColumnValue(0, new DataSetColumn("value", "value", String.class), "sí"); DelimitedTextReportRenderer renderer = new CsvReportRenderer(); ByteArrayOutputStream out = new ByteArrayOutputStream(); renderer.writeDataSet(ds, out, "", ",", "\n", characterEncoding, null, null); byte[] expected = "value\nsí\n".getBytes(Charset.forName(characterEncoding)); byte[] actual = out.toByteArray(); assertThat(actual.length, is(expected.length)); for (int i = 0; i < actual.length; ++i) { assertThat(actual[i], is(expected[i])); } } @Test public void writeDataSet_shouldFilterBlacklistedCharacters() throws Exception { String actual = writeSingleColumnWithBlacklist("Yes: ÀÁÂÃÄàáâãä, No: ♪�", "[^\\p{InBasicLatin}\\p{InLatin-1Supplement}]"); assertThat(actual, is("\"value\"\n\"Yes: ÀÁÂÃÄàáâãä, No: ???\"\n")); } @Test public void writeDataSet_shouldNotFilterWithNoBlacklist() throws Exception { String actual = writeSingleColumnWithBlacklist("Yes: ÀÁÂÃÄàáâãä, No: ♪�", null); assertThat(actual, is("\"value\"\n\"Yes: ÀÁÂÃÄàáâãä, No: ♪�\"\n")); } @Test public void shouldLocalizeColumnHeaders() throws Exception { String startingLocale = TestUtil.getGlobalProperty(ReportingConstants.DEFAULT_LOCALE_GP_NAME); SimpleDataSet ds = new SimpleDataSet(null, null); ds.addColumnValue(0, new DataSetColumn("all", "reporting.all", String.class), "All Value"); ds.addColumnValue(0, new DataSetColumn("reporting.none", null, String.class), "None Value"); TestUtil.updateGlobalProperty(ReportingConstants.DEFAULT_LOCALE_GP_NAME, "en"); assertThat(renderDataSet(ds, null), startsWith("\"All\",\"None\"")); TestUtil.updateGlobalProperty(ReportingConstants.DEFAULT_LOCALE_GP_NAME, "fr"); assertThat(renderDataSet(ds, null), startsWith("\"Tous\",\"Aucune\"")); TestUtil.updateGlobalProperty(ReportingConstants.DEFAULT_LOCALE_GP_NAME, startingLocale); } private String writeSingleColumnWithBlacklist(String value, String blacklistRegex) throws IOException { SimpleDataSet ds = new SimpleDataSet(null, null); ds.addColumnValue(0, new DataSetColumn("value", "value", String.class), value); return renderDataSet(ds, blacklistRegex); } private String renderDataSet(DataSet ds, String blacklistRegex) throws IOException { DelimitedTextReportRenderer renderer = new CsvReportRenderer(); ByteArrayOutputStream out = new ByteArrayOutputStream(); renderer.writeDataSet(ds, out, "\"", ",", "\n", "UTF-8", blacklistRegex != null ? Pattern.compile(blacklistRegex) : null, null); return out.toString("UTF-8"); } private ReportDefinition reportDefinitionWithOneDSD() { ReportDefinition reportDefinition = new ReportDefinition(); reportDefinition.setName("Testing"); reportDefinition.addDataSetDefinition("one", new SqlDataSetDefinition("one", "description", "select patient_id from patient"), null); return reportDefinition; } private ReportDefinition reportDefinitionWithTwoDSDs() { ReportDefinition reportDefinition = new ReportDefinition(); reportDefinition.setName("Testing"); reportDefinition.addDataSetDefinition("collision with \"tricky!\" tough characters", new SqlDataSetDefinition("one", "description", "select patient_id from patient"), null); reportDefinition.addDataSetDefinition("collision with tricky tough characters", new SqlDataSetDefinition("two", "description", "select location_id from location"), null); return reportDefinition; } private ReportRequest requestFor(ReportDefinition definition) { ReportRequest request = new ReportRequest(); request.setReportDefinition(Mapped.noMappings(definition)); request.setRenderingMode(new RenderingMode()); return request; } }