From 0b1ffe9f29476e70400d1f2576fcb4df3d9a672b Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Mon, 16 Apr 2018 16:24:09 +0100 Subject: [PATCH] REPO-3424 Unit tests for docker transformers --- alfresco-docker-alfresco-pdf-renderer/pom.xml | 10 +- .../AlfrescoPdfRendererController.java | 3 +- .../resources/templates/transformForm.html | 19 +- .../AlfrescoPdfRendererControllerTest.java | 52 ++++ .../AlfrescoPdfRendererHttpRequestTest.java | 51 +++ .../src/test/resources/quick.pdf | Bin 0 -> 23697 bytes .../src/test/resources/quick.png | Bin 0 -> 28540 bytes alfresco-docker-imagemagick/pom.xml | 10 +- .../transformer/ImageMagickController.java | 9 +- .../resources/templates/transformForm.html | 1 + .../ImageMagickControllerTest.java | 116 +++++++ .../ImageMagickHttpRequestTest.java | 51 +++ .../src/test/resources/quick.jpg | Bin 0 -> 26445 bytes .../src/test/resources/quick.png | Bin 0 -> 28540 bytes alfresco-docker-libreoffice/pom.xml | 10 +- .../JodConverterSharedInstance.java | 2 +- .../transformer/LibreOfficeController.java | 45 ++- .../resources/templates/transformForm.html | 1 + .../LibreOfficeControllerTest.java | 130 ++++++++ .../LibreOfficeHttpRequestTest.java | 50 +++ .../src/test/resources/quick.doc | Bin 0 -> 19968 bytes .../src/test/resources/quick.pdf | Bin 0 -> 23697 bytes alfresco-transformer-base/pom.xml | 16 + .../base/AbstractTransformerController.java | 20 +- .../alfresco/transformer/base/LogEntry.java | 55 ++-- .../transformer/base/TransformException.java | 1 + .../src/main/resources/templates/error.html | 9 +- .../transformer/AbstractHttpRequestTest.java | 134 ++++++++ .../AbstractTransformerControllerTest.java | 292 ++++++++++++++++++ .../AbstractTransformerControllerTest | 0 pom.xml | 1 - 31 files changed, 1027 insertions(+), 61 deletions(-) create mode 100644 alfresco-docker-alfresco-pdf-renderer/src/test/java/org/alfresco/transformer/AlfrescoPdfRendererControllerTest.java create mode 100644 alfresco-docker-alfresco-pdf-renderer/src/test/java/org/alfresco/transformer/AlfrescoPdfRendererHttpRequestTest.java create mode 100644 alfresco-docker-alfresco-pdf-renderer/src/test/resources/quick.pdf create mode 100644 alfresco-docker-alfresco-pdf-renderer/src/test/resources/quick.png create mode 100644 alfresco-docker-imagemagick/src/test/java/org/alfresco/transformer/ImageMagickControllerTest.java create mode 100644 alfresco-docker-imagemagick/src/test/java/org/alfresco/transformer/ImageMagickHttpRequestTest.java create mode 100644 alfresco-docker-imagemagick/src/test/resources/quick.jpg create mode 100644 alfresco-docker-imagemagick/src/test/resources/quick.png create mode 100644 alfresco-docker-libreoffice/src/test/java/org/alfresco/transformer/LibreOfficeControllerTest.java create mode 100644 alfresco-docker-libreoffice/src/test/java/org/alfresco/transformer/LibreOfficeHttpRequestTest.java create mode 100644 alfresco-docker-libreoffice/src/test/resources/quick.doc create mode 100644 alfresco-docker-libreoffice/src/test/resources/quick.pdf create mode 100644 alfresco-transformer-base/src/test/java/org/alfresco/transformer/AbstractHttpRequestTest.java create mode 100644 alfresco-transformer-base/src/test/java/org/alfresco/transformer/AbstractTransformerControllerTest.java create mode 100644 alfresco-transformer-base/src/test/java/org/org/alfresco/transformer/AbstractTransformerControllerTest diff --git a/alfresco-docker-alfresco-pdf-renderer/pom.xml b/alfresco-docker-alfresco-pdf-renderer/pom.xml index 0e95212e..a88a499a 100644 --- a/alfresco-docker-alfresco-pdf-renderer/pom.xml +++ b/alfresco-docker-alfresco-pdf-renderer/pom.xml @@ -4,8 +4,6 @@ Alfresco Docker Pdf Renderer jar - 0.9-SNAPSHOT - org.alfresco alfresco-docker-transformers @@ -50,6 +48,14 @@ alfresco-core ${dependency.alfresco-core.version} + + org.alfresco + alfresco-transformer-base + ${project.version} + tests + test-jar + test + diff --git a/alfresco-docker-alfresco-pdf-renderer/src/main/java/org/alfresco/transformer/AlfrescoPdfRendererController.java b/alfresco-docker-alfresco-pdf-renderer/src/main/java/org/alfresco/transformer/AlfrescoPdfRendererController.java index cbf1815f..7f605c18 100644 --- a/alfresco-docker-alfresco-pdf-renderer/src/main/java/org/alfresco/transformer/AlfrescoPdfRendererController.java +++ b/alfresco-docker-alfresco-pdf-renderer/src/main/java/org/alfresco/transformer/AlfrescoPdfRendererController.java @@ -103,6 +103,7 @@ public class AlfrescoPdfRendererController extends AbstractTransformerController @RequestParam("file") MultipartFile sourceMultipartFile, @RequestParam("targetExtension") String targetExtension, @RequestParam(value = "timeout", required = false) Long timeout, + @RequestParam(value = "testDelay", required = false) Long testDelay, @RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "width", required = false) Integer width, @@ -146,6 +147,6 @@ public class AlfrescoPdfRendererController extends AbstractTransformerController executeTransformCommand(properties, targetFile, timeout); - return createAttachment(targetFilename, targetFile); + return createAttachment(targetFilename, targetFile, testDelay); } } diff --git a/alfresco-docker-alfresco-pdf-renderer/src/main/resources/templates/transformForm.html b/alfresco-docker-alfresco-pdf-renderer/src/main/resources/templates/transformForm.html index f142aeee..3d16a701 100644 --- a/alfresco-docker-alfresco-pdf-renderer/src/main/resources/templates/transformForm.html +++ b/alfresco-docker-alfresco-pdf-renderer/src/main/resources/templates/transformForm.html @@ -5,16 +5,17 @@

Alfresco PDF Renderer Test Transformation

- - - + + + + + + - - - - - - + + + +
file *
targetExtension *
timeout
file *
targetExtension *
timeout
testDelay
page
page
width
height
allowEnlargement
maintainAspectRatio
width
height
allowEnlargement
maintainAspectRatio
diff --git a/alfresco-docker-alfresco-pdf-renderer/src/test/java/org/alfresco/transformer/AlfrescoPdfRendererControllerTest.java b/alfresco-docker-alfresco-pdf-renderer/src/test/java/org/alfresco/transformer/AlfrescoPdfRendererControllerTest.java new file mode 100644 index 00000000..15ffbd28 --- /dev/null +++ b/alfresco-docker-alfresco-pdf-renderer/src/test/java/org/alfresco/transformer/AlfrescoPdfRendererControllerTest.java @@ -0,0 +1,52 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import org.junit.Before; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +/** + * Test the AlfrescoPdfRendererControllerTest without a server. + * Super class includes tests for the AbstractTransformerController. + */ +@RunWith(SpringRunner.class) +@WebMvcTest(AlfrescoPdfRendererController.class) +public class AlfrescoPdfRendererControllerTest extends AbstractTransformerControllerTest +{ + @SpyBean + private AlfrescoPdfRendererController controller; + + @Before + public void before() throws IOException + { + super.mockTransformCommand(controller, "pdf", "png", "application/pdf"); + } +} diff --git a/alfresco-docker-alfresco-pdf-renderer/src/test/java/org/alfresco/transformer/AlfrescoPdfRendererHttpRequestTest.java b/alfresco-docker-alfresco-pdf-renderer/src/test/java/org/alfresco/transformer/AlfrescoPdfRendererHttpRequestTest.java new file mode 100644 index 00000000..c4de8383 --- /dev/null +++ b/alfresco-docker-alfresco-pdf-renderer/src/test/java/org/alfresco/transformer/AlfrescoPdfRendererHttpRequestTest.java @@ -0,0 +1,51 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Tests AlfrescoPdfRendererHttpRequestTest with a server test harness. + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public class AlfrescoPdfRendererHttpRequestTest extends AbstractHttpRequestTest +{ + @Override + protected String getTransformerName() + { + return "Alfresco PDF Renderer"; + } + + @Override + protected String getSourceExtension() + { + return "pdf"; + }; +} \ No newline at end of file diff --git a/alfresco-docker-alfresco-pdf-renderer/src/test/resources/quick.pdf b/alfresco-docker-alfresco-pdf-renderer/src/test/resources/quick.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a1779afd8b4d1b7d75456c61900c5cead5444abb GIT binary patch literal 23697 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≺ND&( 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 Pz?x-07<5&PtcCX{dE{yb literal 0 HcmV?d00001 diff --git a/alfresco-docker-alfresco-pdf-renderer/src/test/resources/quick.png b/alfresco-docker-alfresco-pdf-renderer/src/test/resources/quick.png new file mode 100644 index 0000000000000000000000000000000000000000..8f1f89b8fc0bdb70dcf6887e17b2d83389697c76 GIT binary patch literal 28540 zcmV)cK&ZcoP)U?z;NT@A1K&zTI6XRG+RobnpA)HPo1R5cQ5JE~RoNN9QM7XUhr9ny=)sWU2A%s#k z{8wnuN^32pp_Q$Ll;S_w;(s3=k6H+!lmc-Gk-Fm7wymU;;@^`-M8ZmANa;TN*Oo~m zem8K(WlpCOLYh9G&oq5LpYwmyFbu=6EUPYTJ6-wT`I35HW4pStGHlm>U*EDq0*xs-zu%u4(TFIxwoGdU*kw6yJ^92l&%f~8GfzML z%(Kt`{pqKkdw=t)+2cp{`}0Rj0n1W&o$sgSVhty0(QwR`(~K&-_2Q9}Hk1KMqqSDH zErh7szvAo>1OE8f4}c2W&U2Klg%JB!{&2*=-jDsT1qj=!;k>7Q?sM>Q^d( zU-fXsl&{}=?a4a_j~TbV5FxOY^2q-X^?!OF(3TZ_@qxrNn67cq^L~OY zerodjlYITG5YQR{T4`HZLWruJ^Nt(Q_pXnBPOVyNr4cayt0((+IdXRoNf z@aoIbD>v>EEywllQd3w82()XCl&##)wo*#td}6f_Xj>^jfKtkvLNvT7*QYtb&Yxqn6 zX&Ru9yW_Fv{&FE8Ow%RZCCv7W9s-nYI|o`TrBvi69C3kG?v;*dbxhUIGDuWgDoQEJ zeW1&@76ND9yk_F;t$F2f9R`i;+`g`;>}S*(;j-7jq0LfjDr#+qPUoz4T01}}o@fq` zk~0XiEQ?^q&_1oJDoSvL(10nemC_0!&?+1bE6?Kp+OJ6?egp7qnUFwy%U)OAaY56V z^bQ#>{Owrp$KHc(wr zu`hpbS#?+lVcQVXJUt@?DX~k7D{BoYg_J0*G`3wAvXzw5s;bD&&Z!P5L&}<}@`{Se z^2&TD4;X_8R8c;?EA-0Bjni?6@;h)#+7 z_vG#?DwBpGu!@Tct7>Yisw%3hYD$WWs_Md?3^j<@q~zqp*y^&9;*xSLuxcy!7ng=@ zC8c58widFYytuHW+AxfovXX+L3Mr+O!dA9MRFoDJl~x;uR9XuuE6NIT_7po72G0r{ z$Y5n@d1X~;$^M#ZFtvr4gk_DTJ~V+O7+Sv=CZpX^8TjbN>AIS2pb_ zhS)BxTL+~$gj&%GQd+c2iw!uR0ikR6uE?RaI5V z{^F3PuClzSsJJF%xiZpPN<&qb6<5)m~+)kh05g%kxjYrg$q z#;nO7-*o*oD{?F$gjU!}OCe@|_s%mfz4pP&FJ67^MGLm&&3y6CosYe;2(e-MD?_`E zU9+Fnlb`9;ZsfvTTL?AttM}gh;M2P`~rPhUCr^m9L$pKb47an>=TZu#rqPd}>nU2iR?YTL63kypQX;nwb>&iZu5%(ouD_o|zos|2C7Qb}Gkj_7~cqi;-~{`rMxUi{+3 znbW6#bK7|ry**`{E?hqDh{2aU@V7ZXetyA8XForAr4*txd;M$gfAGm`&p-6SdzCmV zB1#BRmb3c#zrE-(m=iJzSC1didHf}hl$pZLU2@?Sm(0qnsV(00_0;LJCVzC}_1CV= ztDW}pABUcJ^)ruM(`(4NyX%~<6-(w%U%h+imK)!7%nH6s&U@am%vy!+MUFJFDQSC=spW_&xYf9ne#_}moaEuDD&*%xdm z(h(_D=dO6*{`+5h<)tTH`L;&IN+{dC>V?0(`0?8>TzK*Mb9R&oA)xl%#~*t7xo7Xa z^Txlv@W!GodCu*P1G7dP8m=cpDXq0qcDT;cT7Pi;#TQ@yf!4aNs#a_L`JERJKI2c8 z)-#{G;mqrv*IM6t^{LlC{KB+JpN#F^`ji`A*IHY3wOZ>v^WQ%D)T{Prt!uZB?Va}N zdQ0ce=ri!Nts$-JcAVaC(A1rFD0lX#?tNzG>!QW)UVPa-VXcc6ec1b`b1Sv}^oEPC z`QwLMx3B*8yQLwAETux0)>_|u{^%2Le^qO}ZN_sg`<<3=h5mZp=wmK@D5tQ(D$Be1 ztYcrBo~gB-_sX@Mjyzv${n6d$UH-_YTI;v&yzruXU)NeM`tHL;+e`MZp3tjRua8#j z({}DTBZf}hR-?7HY-NQkt@X#(UwOr~?`y3$eEH-F=igqVwXWH5%D^@cPFSb4e(APz zhMsfBq^WN&-<+$pwr!=9vMr^xe)ftJ$6xZS*7~tOoO=0V-)XHUJ^F{!uY6i-{m@yX zj=Japt@X^8uIV}Uk_xTYeR%8GOK;X%UpKDLn5!PuT4&CFA*1JsrCLva_S!4&c~fgW z>DzA#tF+d3SZS@x|9HmHug!=sc%IUF`qO_HcJ2dO>+-ynA55I9b?EkMPru>e7p6`6 zZE1)w$}R7J1#n;TJ2Dst+lS* zcE;d=GxK!u;#Wspc!$>d+lNmZeeo@ITIcM@(^|iN*YTJ9>2-%*Gqhh@VG;bxaT(IGmS2we}w|Y?J~1Bq4mYR_Vs|nXwn2b5fIx z4lhnw_{r0kXpI&oFyqttom!4=0f>awtz(pJ6V?GKv<4Lx0clzITDDJ%3C6@Rd&0b6 zOfM7S`kc9N>f3P;OiB9YrRy$#;ENA5nfUqynuKxg_^YD|0896(ueZ4yO z?Add0tF-uV*{0jU~EPdO?S@~Re&3Vfwf0I{DtIo}~&R?)4KR>5D*u7J7 zdh=Et+V>yaGXv?5P0BAUsm5_CA%Kvot+s4n=6B1QrVa`M!Hy>n8@giB4B&=mUTd}W z-B)IZ2X$*14u^ykfYvt9yHERo-v<0Ph5?{_N-G7lZ_~A3@4%ohi?Wm{2Glj^)K%1^47>Ktn?}9-+-pCq%J%zA16p-#zVf~Mo|&Kg^pjTu zm)>!E-qerRmDVm?x3k-@fq)j65^L|0Sw9+MhxY*5_iB@vBxCp6DUPfx&ET4|hQHsa7|5GyM@Q)U{0fMEj8I+{=V6*gc>tEzUtnb5@E zu_;@n^&c{{Q;U?nd-sF{5)H)0QAP!TluB6|iO|{%1Pu^|5JoUYf|90CnwG89ybV*T zAP^TLi%UaLR~b6ts(Xj3Id?w!mGA|GR*}^`5C}g|Q<|F}YTYq`kZlPAz-PoMbH~;_ zRe(~KKc#hI0NS2#TF>gc|N8gNC*FA3FyF0rJeSgMNHSPjNTF;27zpjmyoASTz!wbq zOdvTav}4~^r)|p!q-C@OcCOia?M;vGoAJhkwM9Ok-&y5w5DM4XN&vPLJ~IYD8~&i- zNEJf^h9R8El0^G_5(w9Y4QCE#hs?y*%@TBNY@c~+a~?V~{*qHJ+E|V>r2?O?A%n>+ z(_%Grxah1QFW!FR&hjpUJH--+vokmEc9oKxm!BU(3e#OLtyi%`N?*|D2Yk|}52aSu zX@_ANMr?9S!J>~Id~o8(i?6-${38OQ+5kIy{vGfC_~v`>XQ0c9tH~I4<;h*{zvjl$ z)Qlh|Si=Y}OV8Ld*vHm}{hZ^!c0>(?*+^vkK? zlIn`m;$1r{hh2H2-u2m-Gp~Q<$%mhN_1h}+{<6K*{+7?a@%rq?u6g*~MZ%C;yMv~H zUAcAH9MGS<``Peou4x9@dHeEr?9pJf73E%g+?Ce1)fd8y<{g%z0 z)vRT^Q;zALwR=n7?rngw;{2S#{S{>vmF2}nHAT+as0L|(wYw;9ufFBnqK#8upEUdEEC2A~4gGI8WAu{j8dJ*1OD3e0D4=~tOzyfxwbZR# zv2OOv?>3ax6qfAE*`5dFW$)j=zfePYNm2Hm0wCVBmabYHhA-cFC+4W*`X`II?>+Y1 zrwcE?@0oGE6Ze;dKw6eflfhRsWN=`*&~V;$mzS)VPqUs0K+1p%&Q3dbXwN$ydE(D^ z-Zyt!sj|!VW$ww{S5Y5%1Hu>Qk1g7qxx=n2&nwB_zyAQEu=VV^s#E!?9abn-fGy%w_k8{;m!pMHttr2o!q&1o0QmrV^2t| z+40$DQxiIkzU|sGOsxU)N@ZhqHDjN`77rv%+p7V=+>@FpPtQPS8d*E z#`v4HNLsyNyYj`xBq#3LytB$O+w|?5ziPq8lG^Irl@-Z@?!Nuf7)fGMO7`YOAAL9f zgzNt_u6w#t%8*ha28}&2v1WTTg9;(zQacVEF|2>rSiiQF70-LUz=&Ye7U?xhmgcu^8Q8EbQ~1rM89%31C;4IH;)OYtp{h{e zj4RH!HZPsFdRw32$6$rRcBnFBwMr3-=gnEaEpO21W1A-iKveh>#~(jB(Wkud42_V- zj6W-!HU0aAnXNi??9{o(;331toODuB&9=`zpPJBV^zGN3Wr8V+r%jw=WMrs{k~OOr zrS=@#xoMoz$i`V_BMyasE{t1yNibS#AzgQob~2ruh%KcNS_x@Te9g&yMn1gmq5-M4 z(uNd2^&Axm#cFV@oCuGD!tQl_o{wo))ZCJK8P5HN*FU)U$)zi0( z30t;d2;t@$ICMG$yMb^&#b6|l0_Q&UQ%X6UoTHyt>)}5^DO*Zo?acR9mbJU$jM28O zoQJuAFW&Pany61OQBuzs~C-T!UO(}$Bg_S}YhGBR;R3jweuVwk*KS9TggcKq-wOq(*C{g(01oSW9Us4I#8`+d>+KVR(jE*|x0_(vS|b zmaPRyX-Fw-+frJXrlD;+Y}?W>gw}>>F8}1gdp}w?e*A^kU3~%|q_D$bq;yP;lv3HY zMi_=8Jz*G9*_JT<{t%a0m-gK3438ml3L&bo-L zl$3^Wu7QYBOhYQ$vXmB5It)fkh;7*#A*E3EVX0Ms(8!{n;Mp>5S*j=SD3_~I!ObH=@X36RAzjptg^}}>!L5rS4 zP8-)BZHvZgE{!-0{L<---l!rkduMsA#u9D1bci(%_`2&zAm~BlfNeVtYP9~h)~~b| zIB(k+hI{O$%?pIawn1Vgdb>q@q4%SlOZ-=cYfGlKZ}Fa3=X6ltPF@>(rGZ%C?1+ZdRH0CeMupH5zdk_{B%>o)5>( z2&~TokG{k;TeVdb?5DQQJ*cywhRI*CDG%V^4EA=Ku*-S>MfTR#pV1l()G_rt7H0yu zp%orgb>)2fIXjCEMMGo;hEht)!#8AFqn*4^m9t3|XX)Et$%! z#Q^$$MOKaY58)TLWnK=pbRiB-sdJKtvzBn)wS4vwr;q3KoA~riF1d!O)41zae%u03 z7mk$Qz_tm8-T8uJj3S1~3S-$Qg;wq{U|U!gfUw!ToQkk_u|+tHfc%x*@CPQY1gHxU zwou+{wrVyv-o)}8fUxBhs=&5U3d?d0pKTKk3nzZN3(H131UR?YXiYfm znjyypt~FAMZ42bbb7Ze};`+1LR4Jq+?69n{E%z2>dnZAAix`<(*p>$n3ZXSx)j!|1 zPzu|2DR&RNXDWqld1r7>S{6#XY(`8#w7?x2EDH@v5e~b8in>Oiv23)GN;wlb;b?hp z9$}kZOSt7GW^VmCwSp6UA*X&QZ@yQS*aEN}k$PINEW#F67|U|+wW26&@U7AysLbWg z>v?mcdyQ(?iy}ALs-E%xiD^nB{uP-2{v-Z*2mssU&kr%YBb(PDMsxF}44Fjdv79-Z zPz`|30NnS`FkF|1()DgE=Syq)+;TPuOwTkMCI$d9g~#8ocaxbWXhJs4MsU(e)Pw;{ zA3iTG-iG0)a>Cn$LI9>jD-dq$&h1JerALoK82F^?$&yAr0S+W+jnC)4Ee<7?!YBFU zE6N6O!iDr7O}rljJ|9}41%`8jXtkn1aPH;2 zIg^k_o)n&u6_^G(%I9Txl97fh`G_-AfeI7fifc}0W(|N=7-l_FQlb??;xp>|4~;1X zzhV3uw#?}+ZTip(0BO73sLyf41*obfJ;Dit5fbA6=Hx60F4-2^X4>XCvi7^p^B{MKv6I;#?%Q)*2l8X3XGDF8R zqzzNQr^`_GE@7SG`qNo3h4mF&b{;V`Oq)%+KIAQ8v&mIw;0H9*K4+KU#L={B>Y7~* zQnGM5OV_&$Zdu6eRm7xl=0#Ml<(v5&b1q#onEwN-w=r-8M+_oo16%88UcutMTz)=M zV+cNZjAuV2Ie}X*K`8wGL#Y)2yOz2TS^;cZ&dm7)yK>1%n3a6_1^X@HVo;LiNo4OL z(3wk)XYG6zY~ZX*X&UQ}F{NbQBz7rwWMj4iFbx*WVA*>59m&x{vBMZXvR3fzJcf;D zP~Z8Lkyyxm&!8Y(aMuGASxkG68}6da#LnXWC+InvH6L=u4FKB4 z=Z7LD&!b5@Qer(q41{9Zw>0m;9g`0Ws zDLNmC+QhkMQfAOLnFnrXVIJAD*tnO}7*K9WvrsPAUdvlE>Cv69UgL|UB(>+qFL-+@ z#T&TsO74Dwl(x)%naln}QhOGE#9uyS{|5eW0r&rv7TuWtCO19qxXJ<04nj2+I?SM%C5y7lD4=a{;k1kj2NTWOlXjJL?Fpic{~Je>~~^6d*;dJUg` z&8#(qy>;Af-SDT-tvg5zbr@<@fIDJ}i6QHIE`N~Y&S1u~Jp3U-vA347<0xOqBk$6o z9WOt^_6mSdAq&=$5(^5Ykb-ZX=Brg4(VfN1DDwk+`~-6xrvAVylQ4W_FXZMY=s%c8 zFW{_O$u4$P(}>?De$~jGpZxw=HeCnOs}Co1Vec*|;^j}s$zjVrMjc00Hh{7L-gt)E z&YUumapP&N(LqKYL25h}^cqah6lx64xq`mw#KGIIVsvImSN`%e<9h?dC9r-P6PEDC zN1S&YvA+6#9JU#80^J7k{C!M#gQi0`YaAVh^YNFAF?irpE%nCPh*HXl_q(w`bbRji?SN?|8j*&gM z{uz=BdG;&%oX(ujc;#kF+w%6~NQD+ifo0)u#RcQA>L7vsz0h?4NA*S3klK&a#?!e! zJ^OJ|PmuKP!FfZ;T|<+>oH&Z!qv+a)r*C1>SA?J>J;a_Z0EKy6bQ=j-Jo^c`xoj<9h9*XZ1f2k#(r0YF}e zYyXTNY>jCU&gS{=xbb$nbmN>;3EEJ>-#n%szMW^E0+{m&DQS!vN!NZf=}gyVge|u; zQX_tY_%$@=Rh_Sg<4|{lybPK&0kCSQEuuJ&%9h-DE7x4h>+f*=nP`ph0c5t~ZDp~Qm@L)cw}(dMDm>iE1Ij;dg?cpA*x$c}h-pdG7;@l%|2pwusT3@C zuXHNb2mJ_%A1W&-Dx#taBc2!&1!H?L@l~=d{C=j*1gL;NzQrFpbNbZ?A7-Q*E9z92%88Kpj}^@@8sK+0NGpV)`KSC@5b>hxZy!gyo#e*^XDfSaTI_r7U4sJ zVG@wUM5OK1o`}Kp0UX_hdEely=2aGPXkiOoO>jHpEqoN<-h&Ki(whN2c=SGwyod)+Ln{o~=q&r~;#aa|TBE>cuyG#q=CbO0Hs=xR zrz(r(3t72k%_;w1*m$PFBlNNB?pE)&^9vO5QKz1D!S!`Iuq8VKH2;~zw@iulAd#{sZ z=s>>zJD+{YlyyAw7s8pWSjx01l-1yC$usx!-u=A)DPOPS$$RkwaLS4F?+tL`F&sUV zBp<+nAK9{;CF@zfg!TJavJyZmduj_$|gmzh3~{6f~uWX*ckEM?{lYD$^4oTaPCD`CMZ z7A&Vi5g*|DPx$r|-ujVyZUfl3kU!tc^p%`{9!&$FQ5K>Ze|w+-gFeu3^x5+|}Vf!{F zFQ;91b}r)UNi5pH=3M4Z;gRQP-HC6$;F-rMtN}Ro0*3UVdslGkybEkad>0LxDc;`NssmJdI*!&}(y~pf1Joz9eT}-8PVxNDVH=+^$Hh!hJ|40;N z=4MK3(IJxC(5X44`RvREgQiU=*pCvl?MQk81-b0nOP5}x#MB352uWE!>$Z^?!~N%S z|8fSWk)Oww9dz%DRYbG4Bulel_Vz9wIe8mWN#*WOR#NH+R&*vb~P(jVaGAFKZeb=4U`4x-;q686qn;q zpl4^C7%tP~xf{sr!b>+34zqS8W*TjhP?7*_--Ymz(S-f`umx$UlonB|8Q7gaT~F$0 zPU}Wo3p%txDYRzy7P5*MbOecpD_Dn}0~iz%Y%CkYkMvTh_UDqhm#)1@@v&hQA%Q=M zUY*@sD@VWkcC)LJE=|~-jXsoGom+Nor>G2n0zJF3VI`Fz`gA8NoA^X(E2$`_I!rK@ z{{0D<>|4!@ZCrFN%5LE55}M4_ydrTdy88pM|=jbUKzWkf$7Th6gj>JPbo@VVV zp;8}UDZG44w?*76LRVnIeQhEEZc@5$ZzK;>yZcBTdV#+_P|oodY1eQZc&|tEf*&A- z#gk}%DrXHQYk!Q zz$dxkJVxHec|)+O*|3T(BZ&RCvw$1%>*3e(mw92?ZM$*UO1WbP#{k(j zBHEB?Z>KuV|j z*F$XhkozCzk;gEb5i5x*6x@j49R7#LWxpWwPpnQIP;cVjEXm=H1{|9iJ;Lxta=Kx$ zh6Z&U4xfethBk*sx5g>BhOH4AL%50jhcje6nDYM>L`0$SOrg++EmCichqLvm^h)9V zYh58SJ9kWUMDLCGZ{jy?%Oa*ixpN9b9?*{te)kVP_$~CgD5L0%ppgQlA!>;I8x_m{d;F#&1BKgXrNrmM zkPS{ce?)q=qNW<#M%&a>6IKYp@m6XlVpezsP=2x~hm+lpl;Ij<_9R(QYv7c<6#t4Li)cA_GxL`_r~YST z_5TUKX)jbGY$5o_v{W&*s##dHyA?zJ#e$`Qw$mHU%IQs&{QG+pWR?^_3$c z#WI{q8QLo_p}p%JN6Y+a?AQ-@7laXlnmqn+9?yOZVAXj~PzcCh%q8dX@gjh_P<`zN z*NdnCl){P>cTiZ70vrwj8fCl1INX1&sFEC>-@H7V*FGjWjw{b*>K23~6jklQa!N%g zw`7KUuVc%WFk>!&6?Xky5#dBUfsqOtmREqoA>NTpRUg}nLw&#Kk?a?Jjc^Uq^rA%Gp#bc9;xzsRxXASrnj0PG6x zyo$S?b)N1PZHX?);oKNiEu|5^FdFP#@;ih^2+9iZcj20gD4t7xJb!zf&M5@?(xaLj zgYiztnm`CQB%{6zt+V5t@D>sc*V*KFtEGVeAu$XjAQ*ijotk*Nuo)Nz;Shmj%$?*| zoOS|$;d9#xE0Wr>V>97aoY>cE$XOS~^;SDhZHJy{+N5;<6QX{`>BJHkhPy)RJWbh1 z$*h-Hv7cM-;PjJdl|g*4emZVQ_X&6pkF*YS9M0II01VS>MY=1uQo7Y8BA%)Gx)sus zjAv+wx*)Rf5LrPzm_cWRBn%|(dS*l^F4qD>x}zv3jAYUa*v)C*nb|V~CJT5o|pJ2;sj8qD?qFOPs2Rm0WZzXN|bMznz zGMTf4gf4UsuxveMBFCS=j%92rW9&dy?cm5h%=ng;{TSHOog^A21zCJElP!`-D>!Z- z{u-u!L80Q9lV}mol9_DZOV1&+lFV4h@RR6}R$r1McMAoYmIl)oGw66a2l;Xm?fWyd zCo7ha&>Fp$W!t&ne0Hy7>2^*$i)52k%ZX`*wTIQaIQwj3BnZ~dXKfBgjiptZYe^j& zv}+A>mwOEEVD2iUpApBBT+P=%(rp+6JFspUE7sF_5F`6iTg2*}v`b^&dd8njyudJ6 z_BGc(!wo5P>x2}<`46pD0VwL~+;2}=HZ$fU;yCG4;v|cvv7>;vScISCB&rGt3C5jH z^=4+SX4rAGPj`oDQj)ovN-kv;1@Jc?DqZEM{z>Yil05lxMy|NR$(q zpn32fR+rKw%=lxOmrGI&m*2o%MXJF=SF*l{#ouuDIcz8acj6b{F$l%OFreyy8zbx$QPUFlg?95Q|3)ArDV}$ zUirpj@KZ9{^WhUb^&zS8_(M#djuFqw#mv}3m)1OV2`_xZv=2D>ES`Il*WaKx{o%kGF=uDBi}fxq9+N6Ux@ ztBkL{K*xeN=3O_9hu@@CD;7$ff6FTqU8e5-2x(Bak3aqqk;=RGaLoBE+zyQ? z2ER0Z=e8`e^nSqS&-u%;NN>&9!JKz0dv*Z4{VX{<*u0z0UD>+>0EL7_@35?Zn=a(o zv*?~mm8Sm*^h%-1qRn9X_oAYj5$7|yAO2X@e!!+$&K%3_Pjh5nY9SCy$xhy%!cA}R z$h9<0Kzrfk;X2w6pzk1Fd6CU;QqY{6&m_4yUrge*lX(12j=q%Pqj>2--g=NdF}!>~ zwL2k}E6$*-5WttlUDt8ZwH(=sBaY>y%jp=$+U+i`{7>pZ+KMXK-9Da+KbpCh-?OSrp zV6HinP#NQfa@nP9+fJY<=Z~XxCx-Rs>F3$~DccmG9HxIp+5qmp1%C)Y7*NMce`WMV zE`x7BLYBdsPh*u3g5nA;zKVVs{QWWZWU^&9UAmIJi&L+oe+sAH%B(50^t%?JYj;}3 zbL3GNP*X>fL#Z`-Gju~zUB>Y9IdK@h+p}#Gw&0>$IA$PU&*rIjIjTQT+=sOfprn{f zZlXghwv9Bw=DBA$!=L@Sn)i0A125k%Vx5+xuA{ICS09UIG59><-{rHps5}Oq=5`?owYhBA%xz5p0*Uxz zs4Zq&Hmy=Urpno}pE1Lz_>es|dn<`gZ=jk+{8IRxkINhguU?Ll1WoXU_W{Fw_u_(R zO)!m^{oM8vKxr0VOe3;2W5+IRE_BI(X{4P~hrBv4f1Wf$SR6XDpI*AJshygFD0jS7he=PkwkhOuU zAE#_Ln_pwrZls9?!4?e}>_E?E05@ODtM{_ChTf^rgxErEdl8^4iwUzJNKoLH4LYwD zC_$Vbz<(HO70ybcBw*5{4NG3;@ku=W7mgc*A@IS*iF{nZ*KZQ8VecLg{OKD0dOcOm z>6-42sx$B9MU(7YoiZUvn&my^7c z+n-|gRJLbhC}>KrATK@7l7E9#W;si@vT`*!YgxUKWvc*$#_$m;<;Qib+0NP=M&HP>ExGE? zO#OuNmOOd`v)<$3&*{>WD=*}^D|zB;cePP_`8>`mcf@cuO<~eP*6n2ZVt!oDvJEVl zMX6%eTvo4STQSR)v2-z6wIs(e^9N?k;mKDx@fKP@)?)6wgSi{&cP{Nx0In@ykC#^fMbv0{O@ZIeC?yEp1(WWCGeZ=le7Hwq3 z>O-j2Qb^ddo{ig?JsXisUM>@7F=;m2w=!o5SKme3E-atL%P+BVFF^m38QFzaox$rB z%7!HFxr1l^%*0vDTFQd&`C%K zO~?Ho9=yEloQ-VRi_|1HXJ9X?i&(lACF#(fJy}#)wC_omX5{W>4+({~76T2i)`RhtPZ+V`SMGk{&0tXf6Ke)Q|?=0{s0 z>Z*xLMuo7d@g*Z*^BQ*Vqe%`RWW2F6YgViEZ3g^Neu6e*4TCI+{T(r28?n8Af#84;{XPoke9txBnOEx z+-vq^vtkY12h*iF>z1*v93zpDL-2VcM&NDegrH-GpHnL!CAnMJnhgmY zIhY*_*-_2Vf$Z2ud>VD7R8~_{jr22gBx$jPig|kq7hOz3qz;HnIsJ0)Ir$Fi|=m4FM2X;^*S59=(vXtha+QEz7LB&-a8W-;Q}yMr zewT6E(G_gmbfUv6C=z+oa9miw>1rHNqHvO4wZM>CAP{J+K#0TmDnx`C5u=kECup2w z{=)cwF~|Hx5J^;Ya`ZT;s)GAfu*w!@PWgJvw?Ab5_+#b|6E}Y~W!LiJ(v3HKb{a5aEiA*L`*F(;L_Fb86K}Ybn{Z`;&7&x?*jW<&32cU!)D5<+@8MUYv3H zEwf(((ANKmtbTPg+Ol5|3MegJ{>hqoiHWJ-&wu#g`BPF8ljpB_Y4-jSU9o3J!9KuN zw(_ix(n@O`b;j$f+E!X?tt@3}_rTWLxy1$$31MO?8-iPA zLOU$jT1DtoS}SEo4x(7F9WmNkMG01U_Qe(JfefN;W!awh)S>49r6^@t%C?p5$X^JQ zQbLH`yVvB@RQvpaisD^6_y3$)BL>UB-nM7?{yH1S@M%w~T07T!!iDc?aB_$MO^r&O_sFug{ zI=Xo{u7}<|aGtOLDW&wL_Rhf0kkXxcxL$k(jw1|HItmh|`$|Z|sof!C2DdxGaoI>A zg%BE3D+^!yx6O1DIz2rGQa1A4aTXgMLG(r^YGrZlzILYU6GB85b2Lz<2e zG^FX&TQ#Ix0YeDstkp)ofnI40A<&NHShZ_um}4Q~r86cXQ!hjw ztQ3T4M)|e0)}Qs~bK$tBTBme6q3w|6BK)&zb*Gr!S{~IS>2=$czZ60k!eK!R zZ#pi86lm#bTL>Y95zPV!fd#>{yAE2hJ>)JB2?-IVv~xxXp^>I1+~4+8|M2j8ule&| zgyRhf1XGesX-H$$wlB8Q?u;(|giK8KnM=2Sou`|{TB{zO^Fj0GBU;A>Ro$M6TPA(G zHXKQ&wO^)RH7)4k=#L2t;{RFyYh#9*7%MCzhC~qBkMOLR4=__@#NOAvF|PT%gjPO zrcGD0Z6gdRw`70w?DF?FRb*EL(uOzdl9e;{l@%X;y6%lU*>zBQ(}_#(xM0#7&{iSvO6&V|9ftEu4JU><^nY8`3^5Uey)7v*N`G zn`Y!xtL{xY_#ERYq_he?S@G7#8{YqB$MU`%#@PG5zv%0$zRun8uMU-(@5p z)gz(is}0|LxAn6byVvz=J!0eL=N?$LsHW_P>t=p@T+dUI3@s4fEq(BwrC){(YfzhE z*}K2bvYN*IyjmTrWaWRd@~i$mujm|WPFwN9TN^%pf6a{aW+U4M%3k{Mp09FOtj?P9 z=z@33>x!P6dDEnPb$q9RIlDf+_J_Z=Z8g4CjL%j|NGa_6Hy1wrW#+1B+b5O>x?kM= zI1%3W$;x*=-thjnnJas=8lB=(LdZqyUwLZ9yKDAm*QnZ%j%yMVtNz!PQ~m>JoMZlC z@IIahApt3bcK{G?gyv>1=-TzNQ&Y;HS@0?F?7Y7$DYuVnKWx_0C$5|EDM+QlQpmcZ z={HV$W!&&*{xbBa_hvt|#qM}X-xGFct=wyKY>(5}w`zBd?bCIevX_)=bH)vPUwK@HqHiP=MJo`_*k1x#n z^o~W7t~~mY=Z?JWovEk2zq|0Xp^x5s0fUTs2|+_eb^pk8~J3`}4a~n~xux z5R+e(($!^GbiVwl z(f9X~+2wYV8wOv~QJRJ?B?jUG$^Dw2I4C6s0GM zvw}xHdHidA!iz8X=FebcXTJQW<%^#l|EkV;|L(b82LtiH_M7ovLZdDF#o&DQqW6J| z`~p@Vf5hOSg*_r*s(8r{$Q_yPnYApEGrH!SP)N0GEvXb5-^a zOKP}#P>Si z8QyN_H;b>j;D--SA9z(0A6jDybk))s+lt3^?(Z^~Gh;y|*PigO+COo5H9PZHc5iF&z9J5PP<&Vgh4v>G$0$$;^_&N`{fs6;1L1RQ_6A&kRRtCU`7tuVsj zaANCoK0NutxjSYqDbCWO#(;tC`|Z#A;p} zLG`MyHt#>X&sgAyHiMG=!Vc}8v8}{q>c~G;?4G_dL|w%e9gK^CzRCT%CbjJl8&tL> z{vRxvHrldZ7+!bB6p=W20Z8HIt60>Q*X>hER8?1ZYJ0*NJpw5`DqKX89a6QnvZEQ7NXM*KS!D&Ga;NPkzO;B@0;QG4HN7ueyy^XA zl^D`c)O*KkRkh_-;7|e+(AxAHCJ@qf&g1Lc`V@q0TM7x(g+r16AW3W%lVPZEe5(uR zUNs}U=Y^v_xNe`uDJ)=z>x!(RCiTV zXP?q}T({&VwN`~CgtJaiuVSs8x7@(E=f)kIKwX7QUAynxEbEs36rEu(Gb7 z`S7h{U+)`Bbtn|q?DSKb8V^r-LfvVg?Y7=&MT`C0iTIoS7a5H*11fz zFq3P;bnbV57g_M(mKjUys!r^A3ZUKHMStgLv}M0AgxhtwFPy|wA>Iy1tDvL8W z?O#22*D44~D_l{_xIX7>&V2sng%eiqo%7M6Pxpfrs_oeHqAyQ*;IWApPsuMcrQwA{ z2x?1<%k}_sH%>_GbXpfdbzR5~TR=?OQDa*LpP2J5uxZz# z9CktbPG4{MvU}U!fI!!TY}4_uhn=kp96kk56Ry*?4V*LZytUik|FJj>B}*!bPUv?A zp3g1<;5Fv*X+!J zZl(|K*Cbwn;jGvE-qGm$|3#s-0z-<;nP0rObAE|c5+B#OcY2GOvNdmR{A#bVdZo6S zykqjVs_G7{PPuB(Df#)|Jh1T1*tFyB9&u(sYlNW@Y3ak#E0?~$VM=as-io}XgS%YW zC#{pecImqt=HhFPm6e_`GO=p?l-#AIX2$VdPCLG7>@y4h^2OHmCk?-~o8IxprmuI^ zm5u3sMQqvBmp9I9o8G@mQYzY(LUv7SwR!g^GjcYT`Px4?<`3bL1@CR1yuGHReTxxo zW8#kOIDGx?3E%8mQlwkndDJawrUsFe(6UWhzoAV##>chnl-hG(^UfxE`i2jt7OYQ= zZChKiW^&HTaG>4st$Rpq8-|geKjWRP(+aGzn7EEZ+K(NcQ2zX?k2mLS8rt)w^V_%k zbkT!f?hjtt{m7E){aX3{PVs4lF}BOt zf_>lJzwphN^b;N$Hh${r=jQC+R$`=%ZqdyU8iYbfUwWu!#gv@f4lzv1{4U!H^-gX5 zGip@`Lx^>m-%ZI|QKXum+VjYjyS`jnx;JRXXI7P9)%=h>uXS>>^?7rq?3mY~)p70P z<9z=3vO4S7-lt{w-0h(RT8Nlq+aEb+)87^s7l*8h5?l6bGyc-Pqu1^HeB!R9MY`pk zqyCuelUD7fC+ED72ix*yeY$aK-*#uWjSDCopXu)zjdB0KEZisqTUiK0qHK*R4dnz^ zYOPTqq!0?E8#s;^Dldd^LV*wvLij1Z@uLAp9KGuNmZ`Q@TH6TI6autTN(ia65<*Bs zWW!6PZ0+VOYD1bR+Xhn_^$ln{tZY;Igd5G~ZjfR)Q2~gsvZOTK?fIg16L)v}0D-o( zmO?14KuaMNT4D(2RadqaQfg&`A%qZ8DcjQ8Cryna5(lrfA*Ir`#1ul?wrv>Zl-ajT ztn2&c@t51SEv2?JrZj~Uv4xOYxv}$bVO?ePgXl+P2d)*s$(rL%G%>ef9 zoxMwRAJMFtxBl~cMq_;0FAF!4$&jYfN+V4voVY3{@W~LSlb2@*=>&5+(MM89FHcB- zwzTb+W?ufvg*gQ>YvuVZ$D)-X%t(m56H+9FA<%aHrklc$hIZO@Y>kj+BrZvyl&yvE z8-As2jg(S47aLyql&!VT@Hv*(>-tK0VObg_kj^r#2IuuwT1%v7^|W*d5YiOF3EC7w z`USRDQc5S)9PL2s{8?U3Y{A6wZGrI+2q}cp+LV6BjM|m!Fa7Ge;)EmqKJF1f3+V>S zx#ru}TKEi~(zdPKa6gYJXTNK*3O3BkEilB%=_eIID{`UHl2QerjL8L;|-x8%1 zeGr3@r`7BGFx2W*A^M57BGfu3B*HS1&gge}@5BV9wQMMcXblP>Y#?A zYZR)6xBP4~UXc&&8QH^*>x`CzJdEZd6xtwu2@(Cm_$g8F52St2tzPzpSNbi=dGW7g z`A2JCc>BHl56S8PG}^NNJ$RWtN;!qagdvQ`EaTTE?w98^QancpX^6ut_!B8)WZPPT zkV4CUsfy134qW|eZE3q<9UAABSc1Pz6R1FJY7Oa59Fdeo)CR_^}frCbE|16wV_QRP_5p~9SY!2K6ivirdjJ@y* zHo{eqbx0Z_QzuiJhUeryjMvV&NDE<@a)K!@km6}T2z97Hs>n$85%@BS@?Rg3*{&%~ zDcm)NsDb1TxbElGDui~teun(cl((CXyN-X^kU34)!0qq>0M@T9q@P2hE&Gka>CH>v zbEtuv;612>M;@0u_uuOU0?*RVH@3VV;Tqppb%8KK-6t$YXt@B~bT5|G}-M75=&xW*gW41>nZ)ldryA@5LXb+8wv zMd{IQ*rJM(>cLbI^>7Vgm<{XxaWAr?{)@i-zm3Lq%-<-S7qT_{6%yfF7zJlQd*}~G zf)l1|M7gn?XwQZvZ{4>g+65B{3N^grShzT{Q-^oG1>N8%2m@exKRFlyJ-nC1^x86= zf)w6MWi+%eK97cjYH<7n?~xl9z!+!)v%LrUqMjdV!i?UM0f6JY1HX4*9&{iZ9zAQPc&|-n4+57g^cn3WI?=$`pY=wIv2>anj*a#iqO0UN| zwrLG~3aud?j)a$?CENi|LOwhP-@^=e8cbLQlc5@Jg_cn1b=*s#72FAFQQzvFFcre^ zFyz2nFbZCRN1zA11skCg+zON7S-1lPyaH#y z$#6W(gPE`g?tu<)H>`lCAQ!%XHzDY;Tn&GPV?e;m@HYs-eQ*8{-SA^5qQ(wD7^cGo zunRs0;0aJL9R3VnfrKzfSO8DJBhUx_2pwQJ%mLsSmA#=tZwXhK$ggm$#6ik50a0aBn4bT%_f^XmnI0-61!U>?jDeaI9{U94&1_kHC zqu%l;px{*K3n#t55rg(59RPj$b@aM3(oNjnS)yR9@fJcD1!CS85ArB8@`9n z;Ck2wx5Cv>0MCF4pTKu;8BYexqZoV~PJ~j310Vy2LsR$*Xcz%^ z!34;Mzd)WRPiA5s8_ zhk+35ty%(iKnJ+Z8+rpbdrW-|e*qKTf$!mYcpBaW->>*eG}^M?D1QFCXTUg+kOt+D z1=}D1rSL3tih9EPa1;cf8zh2)42S_B8R9^~c(1#E4nqLA7j6e22_A!4QHCsV0DB z%TnMp_z=!&=w$<{y@LqN(j&OeqfHqJ2{@lPN+9=;@M9gV1onByDj44dE**a@$} ze8B6q-MwK^$Xnq`f-(TIVIypYIPc-7z;iGMHo&IeuGusKR8 zi#+2_JV-R;XY3b!s}53VXODd za@Y!8KsICwD1g@RE}RG*8b0B_jm8|N-!3BG>aSoD6hlvF2J7H`u%IJ+4yAAojD^?X z1sDz&dJ9qlhQd^s139n+{s5if2UrG4uo_0dCYT9r;7q83yI=v_2s!Wpl)+8#0nCR% zkm4;YB)~_I2}y7ZEQU{^1V%yv^oRFg9VEc*9x+XzJ9LK(=wV&Rr8>4z!0pFae68#v8hR1s!1_OowA29+IH~&W`#z zYKVuv@EBAywka0I*!>mdPd1s|+}Cm{q=U<#b?Ez$l8=BzR9|F?_C2&f8z-rO(< zbx|XW$Yi1yCB%C}A!p8Id6U7&nnO+0Sjp#&O8ikx)Iu#J zMP01G=Z!losDgxlFgb`MV}(4Iwli-HLrfF_CWOK8LMtl4@Eq9AEzV5J2X)?!jvc%i zdchw+c>xEGm#{9XIU_263kbj^a0QG63j!V)(QHNw`2Yr^WqyDn9DUV#48}IR(vF($ z9j03Az=ROQLI|oM+3Of4l!JyK1VF+xsDaa>rrQy!LQn&VKgE>eg7^rIf#v{AgI>_` z*Ro}e{<7aLobI9_&g+^^hYNUNR?%JD_P*FE%615Ejur!}pb$2}k)RoqO8vW zL197f{PR!+tzn$kXfUdEpXWF`z}!pN3)^5FjEz{=C4~MYgEU-#j^u)skpy;BBwdon(I&VHy1*kq)Dt4)`Mjmc$P%!_RAjAlE)>JD zUTk(tZ&dy(pwVCUTgL&f+yQ?^4kFQ&@}T#`=>w(kxVK6eT~ax69JyFWWf~vc&;gH) zZZR6sY3BzFSPnQipoxFPsfZ%=9|hSu7z{o*7uqy50W>7$;Q7jKxCl}p6lLrpbVfpj zBO%BCh(U4CHPH_HgQIN@klrEHdVrK7L0#V>0%y#IZvot)k>!6^g&FZW`%6{}+9^Pj1f-4l~ct)j+dq4F*7Pu*YaZ_*(} z+sZ9u>)cUXu;#t*Yhs(FWW*}<^KW1@r_uAlLy8DM2wApq{kr$3=IpG_=+nj!hul9` zYa1a&?!+IqttxNPySZ28`@f;r!(+KYT2|MJ+}8-#^-eKT#>=JICUTL|?}_oqgGStAYwtp(I&Z&~u*6tmvwzIqRt-{FN;y~AwjH*V z(vc^CTTKiIG)YTrmH=qWBhRvxt=t1u-){Zw-Zc#$s2$NMRWI0s1+KMHmaQy1a;%l+ zfNPXeR^;IbA+%CLh{_##mDM3tTb?y_aqfnnQ)|S?ROMuE{dRtNxdN1Pffc0%?~IC; zc0;DzPNpDKn=@nHwz*pYw9+cloU82mGfFqS9U9j$8*ykjAICsapPSBV)-KUnx=z!% z%C?Xont+GnPY0)AXM2 z@#s}4Pf!+m|6@t;CS_RiU8hr7f!YkT7B_X^Vh44>h0Xc$uB+#pTq$iSC5XX|-@~Z>SW6 zl&14Pa4mv_>MdW)Z+Ypd@vYl+KB`aA&#Tq(uR+t{gPL_*;caT*?Q86?U>HBW{18#M zTMF0lM%wrjdW`Q^w$$@+Na<|*;(Sd&BZRYSL}ZR1J#uTDV>aRt5TFHSd{S(jPZ+Xb z!Ll;aJ096q29jc9&AszihGNr$_MWw$u1oCQAtoq=U7fpN_0Acq3ino}b!}nb6m3Qb z5h~fY<%?N+R^`{!lxjofd^vs7(tRxlwc9&=`s&X&qz>$+_HJGI#)8$pV!zPU@wY1;9~HXC2~Wb?wT*w!uLlLAU1rJX%vu}W@X?peEL%8s<& zodT3?`h0F>rJeQFq{7VfKOOspPpHW;H$mp2r%nGF;OE#=o_t}!N!g7@qpV6sVUEa=3 z6Bg~7yGEtANlpvynmB3Yd-HXAQ(s}u;@4)x_2?ECXLz%-@UE|a+`HEfgbJ6wIK^(# zCONir=`&xKh7vN``q#ZXwIUp-*}it|mz$FNv?^M>WW$8Dah*HH`pVaTIHRmeRBcktA6FIbtxE6OqI~VjO*6AqY37QLRyQ5d-ESa-*!{&93*T8z ztglJGw&g3<)dbRhUadHFGec!tzggbo*dtSt&B8@1HcwiVwJ)G1Wwve)H_3E{s&nqvpeKDqG;g9h}uMjlOF&lAc2!!+2K>daw z&iB^mQ+pP!H#_x6urimtxd7Pw`P6;+v>DcJ+Y4{aeq$9vYTFhz6JIBc7}H}^ z%HsQ9E2v6sKR9*U#Kn~sEe5pNJ8eNpsa2bswfXCL6}2X1nM+=o-Tds6TBQ|Fx#evQ zhQ#!nc3nYboxkJBBbu~`_qIwmOl|F7mzjFhm~@@_!^0D6YihE-p0@Jy#WJONN)z9< z4?oE*O&WaDNugDRpy>;1Wug0wWWFfHXYJ4{AlIK3|*c1^{g#3b|!Z1rt^1fpTA4QH;<{?zhl-`+p5i(G<)fL za}x#*PO`R7x&6an-@)m@yhU%!u)}qkUwpsf^TnwnMrOqC`Tm{>K=wD^xrX!8&%}eLJx~S`A<4dM~HT|#O*M=PN*7wpzTh@p} zAu^1Q1hhuMwn*>aGc`Frqiebj*J!ISb5@q$P=&iHx?g@~`<_XWTQWbJV<&cO+c_cF zwtdr1i74r7(IhD?9)(CsO=^>_fzNV5(pz67$rTx7?zO-rEFnP-f?=@s=1+^S;{c&Y$Hv=sOH!In+6Q~Zyb{^fM^)Z86bnVpcsBZC{IwYl4 zmF3qsJUY`HC*Qiy77^3@)DQ2doc!sg+|&`55BL1m zLR&T>sq?XYf-xFMYST2}7eG?G48LC+f%Fce`nK%XJt;A+X}4xcEt{mJ1e^A4Vb_!y z@lD$u)xCMYp7C*kP8W{x?bx=rl9Xl%JKz0u&5y+cuRJ~=H5w@e6Gw8YVH?jqDzXL0KhRzsq%*x^`U2 z^m&CA)myjLCM6^%$2V)+F0NTxdb2of+0m2CMt@l&4hQuHObF>ZM{qp!!t@z{t*lUO zja5|}sxVuRAD$A_TBDTfzyWHC>w^Ax0NZ<9*s|T+aDhhpB;i^sWCNk{iptVTKm_92 zjOY$1r7-Ng8H<_^?$e=Lin5hq3fIZxGlkCp&^}Z8rNFW*;g>+Tth}bmO#qU~=?VC1 zD|E(?f$^`sy3|$4F?2u9bcGz;xYa3w*z_u+R4Ipe`IARQG z=Zsyer1SxXnUa_u6DkiU9C7^d55>=Z@Wma|k3Qw^XBtW&u*%D8s=|(h9R};F!nEw$ zrtQ@AA1rLu*2ox{qRaB4l%lne_P(uJ8bWK7s<%mwH{DsyKpu`-1>jt7*~A74XDlx2 z+OJ99cKbKk8lZaq)1S6H^2*Z?-J&wa= z<+YWyK;g30^yt~dk6ovosz3g=-I}(m-SF9JpmJ*_y*p;43rOueGIiOrlM)9F>N+TW z!-sQ{2Xq0XX=qy;(g`C|m<=KVw9?M3vHc0X3g#^=EC)hG#T6~a^n;phbJujb^^g7f zRL^;0lE!e?9NhGOAspwszz&6g@;&=XcNe(6`;Y>Ja_6Lmfzr-)&Z@Su1}Is-t|q-z z>lVJeS&Qt9{^w5ri$8l^PF5Y#uoY&@u1(w3ZuqpG!Dh{TK<`UNX1?=TrEHZDKxA}G z_GfxZnZIuOCL=yx*H}UdWV8j)b~seOK6psAI%ZE8+ET%0zN}Y2+`20;;Pk%!P(|3X z4cPwDN99dNj5xb%UHRUk-6bIVU2@d6H$JsfJI4i4HVC1?*P>l)%$_YXb^+nan#!F0 zB(!SQDYW6URW4J#Iy6het_Y{M&DcG8zQ51VUMF@0jTAKc{u}YnL2Ip%hA3UVa>I8k zE6ZzQ+qO(?m1Gy^Z1`ecS+#81BO`0t(t@n&CPxhIeM0xV$x|1-Ir=;#YL-yB@#}fTMd6O;jjLVsG7Ub{VK7Av}E!y>Mj?&VUHM_TN_-1KkMJU*|Mf0Hp;zPUF zPu@_Kx!d1+NdMzHY=8HYt!q@T6T4GcwEE5OtK(X??bBRFPE6%VWBX?i!n5 z@0_x~seMY$jK#%8s@Z_1yS`gckP}Ys-XeeA%ItN8%}4dA+q!Az>ODrN zaQB9?o|m7Llp3hsv3>K@Wrf@KC-(2t^@w(c&_Zh=d@cI7$^O0pgGv)fO9@xlZB85< z8x-JcHLUZ#AEqsQc}9(|Y5xn3Eco%qo%438w6vDpnt5L-A%iiMnHzJrR3@a@S<{!6 zR?xKP@@A*CoJkX2N& z&+2u>35f~*+Pv*cpZlSzCS1I9?Z#>AGmab@A1{fDC~S=TZ^U81-Hu#@Dk`<~NfoxW z8B7e?b+uMlqm>X&&j!=<2Yp&8J8VmzDTUCu+dgW9z^*B&_W7*&_dnD8>U+BNh*M#! zuB_6COYr#-h74EMXd#uY{0XsAXgh3aCCz|g*VKe9ErmwQAH6>NPgm{0fPrJ+e9-L)b5JH72YLPfGU~ENuIe;;5;qsj3U6C#jk`rG@E};fg9NtfW5>lN>9gsN219^XjU;rwtKG zX>TdXAz0Pb)RfoxQxi;Y=lFW2VoZc6SiZE{?9i@%6QFFv$`aYOU5|97v>OG|=r3!; zVZrgG+$_NCFK>bx-9*hEE27Fp69o1RUS}75enqU>~-oJ5Qa7O_>+9PE! zJ=qD{&`O(evF?t&S}R*i(_W8 zVuJCpL9OZ&C*3gDh72~K-VtX5rHDXsg4Ws}Q(v&m6_;TKQ%ntkU_D_!qgD{U_}KdV zr`mWx*eeYZ7#Pb!gD_OxZSu z$=;c(OVh3N)_$wBw6rqNVo*BhXni&M%NlW5aNd0#g#-SKoDotI$uNpe5qC|ZF4|Gh z;jB~R>k4baNJDs`6|O5+YwzumZZsx_B+84UBZ&mg^$5q0?zSk!0TJOsih6paz;(;J z4QlF}K8T@6{63Mth&;#nLDt*MsQX=ibZm>XP(e}h;Ne{dHFFZMqa^B4)u2_-S_mpL z_oNIS)T*5pH!>EWWBUS zq@Re0H#Jg{KuFVwi|ugAG0j`WX{C{FK@NAbXDQr!BOb;mtw{q7eQ&?4ReDwC4=b{l zXU4a0=l6n&>jm04$81C+{!`HP2@n5F!uIbF4N^Y;1yR(OHTj1kZ5)?1;=hH+Fu+O8 zvs7fP_iI!3AK}&c*GYJ+cUm3hjVh8;d0^JzZw?XS>vKQAlfresco Docker ImageMagick jar - 0.9-SNAPSHOT - org.alfresco alfresco-docker-transformers @@ -50,6 +48,14 @@ alfresco-core ${dependency.alfresco-core.version} + + org.alfresco + alfresco-transformer-base + ${project.version} + tests + test-jar + test + diff --git a/alfresco-docker-imagemagick/src/main/java/org/alfresco/transformer/ImageMagickController.java b/alfresco-docker-imagemagick/src/main/java/org/alfresco/transformer/ImageMagickController.java index 837b77ff..6f81dbd6 100644 --- a/alfresco-docker-imagemagick/src/main/java/org/alfresco/transformer/ImageMagickController.java +++ b/alfresco-docker-imagemagick/src/main/java/org/alfresco/transformer/ImageMagickController.java @@ -114,6 +114,7 @@ public class ImageMagickController extends AbstractTransformerController @RequestParam("file") MultipartFile sourceMultipartFile, @RequestParam("targetExtension") String targetExtension, @RequestParam(value = "timeout", required = false) Long timeout, + @RequestParam(value = "testDelay", required = false) Long testDelay, @RequestParam(value = "startPage", required = false) Integer startPage, @RequestParam(value = "endPage", required = false) Integer endPage, @@ -138,7 +139,11 @@ public class ImageMagickController extends AbstractTransformerController if (cropGravity != null) { cropGravity = cropGravity.trim(); - if (!cropGravity.isEmpty() && !GRAVITY_VALUES.contains(cropGravity)) + if (cropGravity.isEmpty()) + { + cropGravity = null; + } + else if (!GRAVITY_VALUES.contains(cropGravity)) { throw new TransformException(400, "Invalid cropGravity value"); } @@ -258,6 +263,6 @@ public class ImageMagickController extends AbstractTransformerController executeTransformCommand(properties, targetFile, timeout); - return createAttachment(targetFilename, targetFile); + return createAttachment(targetFilename, targetFile, testDelay); } } diff --git a/alfresco-docker-imagemagick/src/main/resources/templates/transformForm.html b/alfresco-docker-imagemagick/src/main/resources/templates/transformForm.html index a33c40d9..3ced2d95 100644 --- a/alfresco-docker-imagemagick/src/main/resources/templates/transformForm.html +++ b/alfresco-docker-imagemagick/src/main/resources/templates/transformForm.html @@ -8,6 +8,7 @@
file *
targetExtension *
timeout
+
testDelay
startPage
endPage
diff --git a/alfresco-docker-imagemagick/src/test/java/org/alfresco/transformer/ImageMagickControllerTest.java b/alfresco-docker-imagemagick/src/test/java/org/alfresco/transformer/ImageMagickControllerTest.java new file mode 100644 index 00000000..477165da --- /dev/null +++ b/alfresco-docker-imagemagick/src/test/java/org/alfresco/transformer/ImageMagickControllerTest.java @@ -0,0 +1,116 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.io.IOException; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Test the ImageMagickController without a server. + * Super class includes tests for the AbstractTransformerController. + */ +@RunWith(SpringRunner.class) +@WebMvcTest(ImageMagickController.class) +public class ImageMagickControllerTest extends AbstractTransformerControllerTest +{ + @SpyBean + private ImageMagickController controller; + + @Before + public void before() throws IOException + { + super.mockTransformCommand(controller, "jpg", "png", "image/jpg"); + } + + @Test + public void cropGravityGoodTest() throws Exception + { + for (String value: new String[] {"North", "NorthEast", "East", "SouthEast", "South", "SouthWest", "West", "NorthWest", "Center"}) + { + expectedOptions = "-gravity "+value+" +repage"; + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension) + .param("cropGravity", value)) + .andExpect(status().is(200)) + .andExpect(content().bytes(expectedTargetFileBytes)) + .andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); + } + } + + @Test + public void cropGravityBadTest() throws Exception + { + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension) + .param("cropGravity", "badValue")) + .andExpect(status().is(400)); + } + + @Test + public void optionsTest() throws Exception + { + expectedOptions = "-alpha remove -gravity SouthEast -crop 123x456%+90+12 +repage -thumbnail 321x654%!"; + expectedSourceSuffix = "[2-3]"; + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension) + + .param("startPage", "2") + .param("endPage", "3") + + .param("alphaRemove", "true") + .param("autoOrient", "false") + + .param("cropGravity", "SouthEast") + .param("cropWidth", "123") + .param("cropHeight", "456") + .param("cropPercentage", "true") + .param("cropXOffset", "90") + .param("cropYOffset", "12") + + .param("thumbnail", "true") + .param("resizeWidth", "321") + .param("resizeHeight", "654") + .param("resizePercentage", "true") + .param("allowEnlargement", "true") + .param("maintainAspectRatio", "true") + ) + .andExpect(status().is(200)) + .andExpect(content().bytes(expectedTargetFileBytes)) + .andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); + } +} diff --git a/alfresco-docker-imagemagick/src/test/java/org/alfresco/transformer/ImageMagickHttpRequestTest.java b/alfresco-docker-imagemagick/src/test/java/org/alfresco/transformer/ImageMagickHttpRequestTest.java new file mode 100644 index 00000000..16d588fa --- /dev/null +++ b/alfresco-docker-imagemagick/src/test/java/org/alfresco/transformer/ImageMagickHttpRequestTest.java @@ -0,0 +1,51 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Tests AlfrescoPdfRendererHttpRequestTest with a server test harness. + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public class ImageMagickHttpRequestTest extends AbstractHttpRequestTest +{ + @Override + protected String getTransformerName() + { + return "ImageMagick"; + } + + @Override + protected String getSourceExtension() + { + return "jpg"; + }; +} diff --git a/alfresco-docker-imagemagick/src/test/resources/quick.jpg b/alfresco-docker-imagemagick/src/test/resources/quick.jpg new file mode 100644 index 0000000000000000000000000000000000000000..08473b8e8b65fa2ce2cb481ab31e5b18862e4515 GIT binary patch literal 26445 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 Docker LibreOffice jar - 0.9-SNAPSHOT - org.alfresco alfresco-docker-transformers @@ -60,6 +58,14 @@ pdfbox ${dependency.pdfbox.version} + + org.alfresco + alfresco-transformer-base + ${project.version} + tests + test-jar + test + diff --git a/alfresco-docker-libreoffice/src/main/java/org/alfresco/transformer/JodConverterSharedInstance.java b/alfresco-docker-libreoffice/src/main/java/org/alfresco/transformer/JodConverterSharedInstance.java index b87c707f..2b9c1023 100644 --- a/alfresco-docker-libreoffice/src/main/java/org/alfresco/transformer/JodConverterSharedInstance.java +++ b/alfresco-docker-libreoffice/src/main/java/org/alfresco/transformer/JodConverterSharedInstance.java @@ -291,7 +291,7 @@ public class JodConverterSharedInstance implements InitializingBean, DisposableB * (non-Javadoc) * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ - public void afterPropertiesSet() throws Exception + public void afterPropertiesSet() { // isAvailable defaults to false afterPropertiesSet. It only becomes true on successful completion of this method. this.isAvailable = false; diff --git a/alfresco-docker-libreoffice/src/main/java/org/alfresco/transformer/LibreOfficeController.java b/alfresco-docker-libreoffice/src/main/java/org/alfresco/transformer/LibreOfficeController.java index 5378a56a..304a9d99 100644 --- a/alfresco-docker-libreoffice/src/main/java/org/alfresco/transformer/LibreOfficeController.java +++ b/alfresco-docker-libreoffice/src/main/java/org/alfresco/transformer/LibreOfficeController.java @@ -71,16 +71,17 @@ public class LibreOfficeController extends AbstractTransformerController logEnterpriseLicenseMessage(); logger.info("This transformer uses LibreOffice from The Document Foundation. See the license at https://www.libreoffice.org/download/license/ or in /libreoffice.txt"); logger.info("-------------------------------------------------------------------------------------------------------------------------------------------------------"); - setJodConverter(createJodConverter()); } - private static JodConverter createJodConverter() throws Exception + private static JodConverter createJodConverter(Long taskExecutionTimeout) { + String timeout = taskExecutionTimeout <= 0 ? "120000" : taskExecutionTimeout.toString(); + JodConverterSharedInstance jodconverter = new JodConverterSharedInstance(); jodconverter.setOfficeHome(OFFICE_HOME); // jodconverter.officeHome jodconverter.setMaxTasksPerProcess("200"); // jodconverter.maxTasksPerProcess - jodconverter.setTaskExecutionTimeout("120000"); // jodconverter.maxTasksPerProcess + jodconverter.setTaskExecutionTimeout(timeout); // jodconverter.maxTaskExecutionTimeout jodconverter.setTaskQueueTimeout("30000"); // jodconverter.taskQueueTimeout jodconverter.setConnectTimeout("28000"); // jodconverter.connectTimeout jodconverter.setPortNumbers("8100"); // jodconverter.portNumbers @@ -96,6 +97,19 @@ public class LibreOfficeController extends AbstractTransformerController this.jodconverter = jodconverter; } + /** + * Jodconverter timeouts are per OfficeManager, so we would need multiple OfficeManagers if we + * have different timeouts. Alfresco only has one. So we delay building it until the first request. + * This was not done previously. + */ + private synchronized void setJodConverterOnFirstRequest(Long timeout) + { + if (jodconverter == null) + { + setJodConverter(createJodConverter(timeout)); + } + } + @Override protected String getTransformerName() { @@ -118,29 +132,30 @@ public class LibreOfficeController extends AbstractTransformerController public ResponseEntity transform(HttpServletRequest request, @RequestParam("file") MultipartFile sourceMultipartFile, @RequestParam("targetExtension") String targetExtension, - @RequestParam(value = "timeout", required = false) Long timeout) + @RequestParam(value = "timeout", required = false) Long timeout, + @RequestParam(value = "testDelay", required = false) Long testDelay) { String targetFilename = createTargetFileName(sourceMultipartFile, targetExtension); File sourceFile = createSourceFile(request, sourceMultipartFile); File targetFile = createTargetFile(request, targetFilename); // Both files are deleted by TransformInterceptor.afterCompletion - executeTransformCommand(sourceFile, targetFile); + executeTransformCommand(sourceFile, targetFile, timeout); - return createAttachment(targetFilename, targetFile); + return createAttachment(targetFilename, targetFile, testDelay); } - protected void executeTransformCommand(File sourceFile, File targetFile) + protected void executeTransformCommand(File sourceFile, File targetFile, Long timeout) { + timeout = timeout != null && timeout > 0 ? timeout : 0; + try { - OfficeManager officeManager = jodconverter.getOfficeManager(); - OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager); - converter.convert(sourceFile, targetFile); + convert(sourceFile, targetFile, timeout); } catch (OfficeException e) { - throw new TransformException(500, "LibreOffice server conversion failed: \n"+ + throw new TransformException(400, "LibreOffice server conversion failed: \n"+ " from file: " + sourceFile + "\n" + " to file: " + targetFile, e); @@ -169,6 +184,14 @@ public class LibreOfficeController extends AbstractTransformerController } } + void convert(File sourceFile, File targetFile, long timeout) + { + setJodConverterOnFirstRequest(timeout); + OfficeManager officeManager = jodconverter.getOfficeManager(); + OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager); + converter.convert(sourceFile, targetFile); + } + /** * This method produces an empty PDF file at the specified File location. * Apache's PDFBox is used to create the PDF file. diff --git a/alfresco-docker-libreoffice/src/main/resources/templates/transformForm.html b/alfresco-docker-libreoffice/src/main/resources/templates/transformForm.html index 40667bf7..82995888 100644 --- a/alfresco-docker-libreoffice/src/main/resources/templates/transformForm.html +++ b/alfresco-docker-libreoffice/src/main/resources/templates/transformForm.html @@ -8,6 +8,7 @@
file *
targetExtension *
timeout
+
testDelay
diff --git a/alfresco-docker-libreoffice/src/test/java/org/alfresco/transformer/LibreOfficeControllerTest.java b/alfresco-docker-libreoffice/src/test/java/org/alfresco/transformer/LibreOfficeControllerTest.java new file mode 100644 index 00000000..b7bc2439 --- /dev/null +++ b/alfresco-docker-libreoffice/src/test/java/org/alfresco/transformer/LibreOfficeControllerTest.java @@ -0,0 +1,130 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import org.artofsolving.jodconverter.office.OfficeException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.stubbing.Answer; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.util.Arrays; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Test the LibreOfficeController without a server. + * Super class includes tests for the AbstractTransformerController. + */ +@RunWith(SpringRunner.class) +@WebMvcTest(LibreOfficeControllerTest.class) +public class LibreOfficeControllerTest extends AbstractTransformerControllerTest +{ + @SpyBean + private LibreOfficeController controller; + + @Before + public void before() throws IOException + { + sourceExtension = "doc"; + targetExtension = "pdf"; + sourceMimetype = "application/msword"; + + // The following is based on super.mockTransformCommand(...) + // This is because LibreOffice used JodConverter rather than a RuntimeExec + + expectedSourceFileBytes = Files.readAllBytes(getTestFile("quick."+sourceExtension, true).toPath()); + expectedTargetFileBytes = Files.readAllBytes(getTestFile("quick."+targetExtension, true).toPath()); + sourceFile = new MockMultipartFile("file", "quick."+sourceExtension, sourceMimetype, expectedSourceFileBytes); + + doAnswer((Answer) invocation -> + { + File sourceFile = invocation.getArgumentAt(0, File.class); + File targetFile = invocation.getArgumentAt(1, File.class); + + assertNotNull(sourceFile); + assertNotNull(targetFile); + + Long actualTimeout = invocation.getArgumentAt(2, Long.class); + assertNotNull(actualTimeout); + if (expectedTimeout != null) + { + assertEquals("expectedTimeout", expectedTimeout, actualTimeout); + } + + // Copy a test file into the target file location if it exists + String actualTarget = targetFile.getAbsolutePath(); + int i = actualTarget.lastIndexOf('_'); + if (i >= 0) + { + String testFilename = actualTarget.substring(i+1); + File testFile = getTestFile(testFilename, false); + if (testFile != null) + { + FileChannel source = new FileInputStream(testFile).getChannel(); + FileChannel target = new FileOutputStream(targetFile).getChannel(); + target.transferFrom(source, 0, source.size()); + } + } + + // Check the supplied source file has not been changed. + byte[] actualSourceFileBytes = Files.readAllBytes(sourceFile.toPath()); + assertTrue("Source file is not the same", Arrays.equals(expectedSourceFileBytes, actualSourceFileBytes)); + + return null; + }).when(controller).convert(any(), any(), anyLong()); + } + + @Test + @Override + public void badExitCodeTest() throws Exception + { + doThrow(OfficeException.class).when(controller).convert(any(), any(), anyLong()); + + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", "xxx")) + .andExpect(status().is(400)) + .andExpect(status().reason(containsString("LibreOffice - LibreOffice server conversion failed:"))); + } +} diff --git a/alfresco-docker-libreoffice/src/test/java/org/alfresco/transformer/LibreOfficeHttpRequestTest.java b/alfresco-docker-libreoffice/src/test/java/org/alfresco/transformer/LibreOfficeHttpRequestTest.java new file mode 100644 index 00000000..b4a898fd --- /dev/null +++ b/alfresco-docker-libreoffice/src/test/java/org/alfresco/transformer/LibreOfficeHttpRequestTest.java @@ -0,0 +1,50 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Tests AlfrescoPdfRendererHttpRequestTest with a server test harness. + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class LibreOfficeHttpRequestTest extends AbstractHttpRequestTest +{ + @Override + protected String getTransformerName() + { + return "LibreOffice"; + } + + @Override + protected String getSourceExtension() + { + return "doc"; + }; +} \ No newline at end of file diff --git a/alfresco-docker-libreoffice/src/test/resources/quick.doc b/alfresco-docker-libreoffice/src/test/resources/quick.doc new file mode 100644 index 0000000000000000000000000000000000000000..eb307fb2182f673a4e48d950b629612925aed126 GIT binary patch literal 19968 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≺ND&( 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 Pz?x-07<5&PtcCX{dE{yb literal 0 HcmV?d00001 diff --git a/alfresco-transformer-base/pom.xml b/alfresco-transformer-base/pom.xml index 6ac34611..20ecb735 100644 --- a/alfresco-transformer-base/pom.xml +++ b/alfresco-transformer-base/pom.xml @@ -41,4 +41,20 @@ + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + + test-jar + + + + + + diff --git a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/AbstractTransformerController.java b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/AbstractTransformerController.java index 8b0dfef0..cf64cf81 100644 --- a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/AbstractTransformerController.java +++ b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/AbstractTransformerController.java @@ -241,10 +241,10 @@ public abstract class AbstractTransformerController if (sourceFilename != null && !sourceFilename.isEmpty()) { String ext = StringUtils.getFilenameExtension(sourceFilename); - if (ext != null && !ext.isEmpty()) - { - targetFilename =sourceFilename.substring(0, sourceFilename.length()-ext.length()-1)+'.'+targetExtension; - } + targetFilename = (ext != null && !ext.isEmpty() + ? sourceFilename.substring(0, sourceFilename.length()-ext.length()-1) + : sourceFilename)+ + '.'+targetExtension; } return targetFilename; } @@ -330,7 +330,6 @@ public abstract class AbstractTransformerController else { throw new TransformException(500, "Could not read the target file: " + file.getPath()); - } } catch (MalformedURLException e) @@ -339,14 +338,14 @@ public abstract class AbstractTransformerController } } - protected void executeTransformCommand(Map properties, File targetFile, Long timeout) + public void executeTransformCommand(Map properties, File targetFile, Long timeout) { - long timeoutMs = timeout != null && timeout > 0 ? timeout : 0; - RuntimeExec.ExecutionResult result = transformCommand.execute(properties, timeoutMs); + timeout = timeout != null && timeout > 0 ? timeout : 0; + RuntimeExec.ExecutionResult result = transformCommand.execute(properties, timeout); if (result.getExitValue() != 0 && result.getStdErr() != null && result.getStdErr().length() > 0) { - throw new TransformException(400, "Transformer exit code was not 0: \n" + result); + throw new TransformException(400, "Transformer exit code was not 0: \n" + result.getStdErr()); } if (!targetFile.exists() || targetFile.length() == 0) @@ -355,7 +354,7 @@ public abstract class AbstractTransformerController } } - protected ResponseEntity createAttachment(String targetFilename, File targetFile) + protected ResponseEntity createAttachment(String targetFilename, File targetFile, Long testDelay) { try { @@ -365,6 +364,7 @@ public abstract class AbstractTransformerController "attachment; filename*= UTF-8''" + targetFilename).body(targetResource); LogEntry.setTargetSize(targetFile.length()); LogEntry.setStatusCodeAndMessage(200, "Success"); + LogEntry.addDelay(testDelay); return body; } catch (UnsupportedEncodingException e) diff --git a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/LogEntry.java b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/LogEntry.java index d8600320..c520ecca 100644 --- a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/LogEntry.java +++ b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/LogEntry.java @@ -67,6 +67,7 @@ public class LogEntry private long durationStreamIn; private long durationTransform; private long durationStreamOut; + private long durationDelay; private String source; private long sourceSize; @@ -152,10 +153,40 @@ public class LogEntry logEntry.durationTransform = System.currentTimeMillis() - logEntry.start - logEntry.durationStreamIn; } + // In order to test connection timeouts, a testDelay may be added as a request parameter. + // This method waits for this period to end. It is in this class as all the times are recorded here. + public static void addDelay(Long testDelay) + { + if (testDelay != null && testDelay > 0) + { + currentLogEntry.get().addDelayInternal(testDelay); + } + } + + private void addDelayInternal(Long testDelay) + { + durationDelay = testDelay - System.currentTimeMillis() + start; + if (durationDelay > 0) + { + try + { + Thread.sleep(durationDelay); + } + catch (InterruptedException ignore) + { + Thread.currentThread().interrupt(); + } + } + else + { + durationDelay = 0; + } + } + public static void complete() { LogEntry logEntry = currentLogEntry.get(); - logEntry.durationStreamOut = System.currentTimeMillis() - logEntry.start - logEntry.durationStreamIn - logEntry.durationTransform; + logEntry.durationStreamOut = System.currentTimeMillis() - logEntry.start - logEntry.durationStreamIn - logEntry.durationTransform - logEntry.durationDelay; currentLogEntry.remove(); if (AbstractTransformerController.logger != null && AbstractTransformerController.logger.isDebugEnabled()) @@ -181,23 +212,11 @@ public class LogEntry public String getDuration() { - return time(durationStreamIn + durationTransform + durationStreamOut)+" ("+ - time(durationStreamIn)+' '+time(durationTransform)+' '+time(durationStreamOut)+")"; - } - - public long getDurationStreamIn() - { - return durationStreamIn; - } - - public long getDurationTransform() - { - return durationTransform; - } - - public long getDurationStreamOut() - { - return durationStreamOut; + return time(durationStreamIn + durationTransform + durationDelay + durationStreamOut)+" ("+ + time(durationStreamIn)+' '+ + time(durationTransform)+' '+ + (durationDelay > 0 ? time(durationDelay)+' ' : "")+ + time(durationStreamOut)+")"; } public String getSource() diff --git a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/TransformException.java b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/TransformException.java index e3a67172..0f6b9e00 100644 --- a/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/TransformException.java +++ b/alfresco-transformer-base/src/main/java/org/alfresco/transformer/base/TransformException.java @@ -38,6 +38,7 @@ public class TransformException extends RuntimeException public TransformException(int statusCode, String message, Throwable cause) { super(message, cause); + this.statusCode = statusCode; } public int getStatusCode() diff --git a/alfresco-transformer-base/src/main/resources/templates/error.html b/alfresco-transformer-base/src/main/resources/templates/error.html index b6cd4be7..54b0facd 100644 --- a/alfresco-transformer-base/src/main/resources/templates/error.html +++ b/alfresco-transformer-base/src/main/resources/templates/error.html @@ -2,8 +2,13 @@
-

-

+
+

Error Page

+
+
+

+

+

diff --git a/alfresco-transformer-base/src/test/java/org/alfresco/transformer/AbstractHttpRequestTest.java b/alfresco-transformer-base/src/test/java/org/alfresco/transformer/AbstractHttpRequestTest.java new file mode 100644 index 00000000..f45fcbe1 --- /dev/null +++ b/alfresco-transformer-base/src/test/java/org/alfresco/transformer/AbstractHttpRequestTest.java @@ -0,0 +1,134 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.util.LinkedMultiValueMap; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.springframework.test.util.AssertionErrors.assertTrue; + +/** + * Super class for testing controllers with a server. Includes tests for the AbstractTransformerController itself. + * Note: Currently uses json rather than HTML as json is returned by this spring boot test harness. + */ +public abstract class AbstractHttpRequestTest +{ + @LocalServerPort + private int port; + + @Autowired + private TestRestTemplate restTemplate; + + protected abstract String getTransformerName(); + + protected abstract String getSourceExtension(); + + @Test + public void testPageExists() throws Exception + { + String result = restTemplate.getForObject("http://localhost:" + port + "/", String.class); + + String title = getTransformerName() + ' ' + "Test Transformation"; + assertTrue("\"" + title + "\" should be part of the page title", result.contains(title)); + } + + @Test + public void logPageExists() throws Exception + { + String result = restTemplate.getForObject("http://localhost:" + port + "/log", String.class); + + String title = getTransformerName() + ' ' + "Log"; + assertTrue("\"" + title + "\" should be part of the page title", result.contains(title)); + } + + @Test + public void errorPageExists() throws Exception + { + String result = restTemplate.getForObject("http://localhost:" + port + "/error", String.class); + + String title = getTransformerName() + ' ' + "Error Page"; + assertTrue("\"" + title + "\" should be part of the page title", result.contains("Error Page")); + } + + @Test + public void noFileError() throws Exception + { + // Transformer name is not part of the title as this is checked by another handler + assertTransformError(false, + "Required request part 'file' is not present"); + } + + @Test + public void noTargetExtensionError() throws Exception + { + assertMissingParameter("targetExtension"); + } + + protected void assertMissingParameter(String name) throws IOException + { + assertTransformError(true, + getTransformerName() + " - Request parameter " + name + " is missing"); + } + + protected void assertTransformError(boolean addFile, String errorMessage) throws IOException + { + LinkedMultiValueMap parameters = new LinkedMultiValueMap(); + if (addFile) + { + parameters.add("file", new org.springframework.core.io.ClassPathResource("quick."+getSourceExtension())); + } + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + HttpEntity> entity = new HttpEntity>(parameters, headers); + ResponseEntity response = restTemplate.exchange("/transform", HttpMethod.POST, entity, String.class, ""); + assertEquals(errorMessage, getErrorMessage(response.getBody())); + } + + // Strip out just the error message from the returned json content body + // Had been expecting the Error page to be returned, but we end up with the json in this test harness. + // Is correct if run manually, so not worrying too much about this. + private String getErrorMessage(String content) throws IOException + { + String message = ""; + int i = content.indexOf("\"message\":\""); + if (i != -1) + { + int j = content.indexOf("\",\"path\":", i); + if (j != -1) + { + message = content.substring(i+11, j); + } + } + return message; + } +} \ No newline at end of file diff --git a/alfresco-transformer-base/src/test/java/org/alfresco/transformer/AbstractTransformerControllerTest.java b/alfresco-transformer-base/src/test/java/org/alfresco/transformer/AbstractTransformerControllerTest.java new file mode 100644 index 00000000..40dfcd96 --- /dev/null +++ b/alfresco-transformer-base/src/test/java/org/alfresco/transformer/AbstractTransformerControllerTest.java @@ -0,0 +1,292 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transformer; + +import org.alfresco.transformer.base.AbstractTransformerController; +import org.alfresco.util.exec.RuntimeExec; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.Map; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Super class for testing controllers without a server. Includes tests for the AbstractTransformerController itself. + */ +public abstract class AbstractTransformerControllerTest +{ + @Autowired + protected MockMvc mockMvc; + + @Mock + private RuntimeExec mockTransformCommand; + + @Mock + private RuntimeExec mockCheckCommand; + + @Mock + private RuntimeExec.ExecutionResult mockExecutionResult; + + protected String sourceExtension; + protected String targetExtension; + protected String sourceMimetype; + + protected MockMultipartFile sourceFile; + protected String expectedOptions; + protected String expectedSourceSuffix; + protected Long expectedTimeout = 0L; + protected byte[] expectedSourceFileBytes; + protected byte[] expectedTargetFileBytes; + + private AbstractTransformerController controller; + + // Called by sub class + public void mockTransformCommand(AbstractTransformerController controller, String sourceExtension, String targetExtension, String sourceMimetype) throws IOException + { + this.controller = controller; + this.sourceExtension = sourceExtension; + this.targetExtension = targetExtension; + this.sourceMimetype = sourceMimetype; + + expectedOptions = null; + expectedSourceSuffix = null; + expectedSourceFileBytes = Files.readAllBytes(getTestFile("quick."+sourceExtension, true).toPath()); + expectedTargetFileBytes = Files.readAllBytes(getTestFile("quick."+targetExtension, true).toPath()); + sourceFile = new MockMultipartFile("file", "quick."+sourceExtension, sourceMimetype, expectedSourceFileBytes); + + controller.setTransformCommand(mockTransformCommand); + controller.setCheckCommand(mockCheckCommand); + + when(mockTransformCommand.execute(anyObject(), anyLong())).thenAnswer(new Answer() + { + public RuntimeExec.ExecutionResult answer(InvocationOnMock invocation) throws Throwable + { + Map actualProperties = invocation.getArgumentAt(0, Map.class); + assertEquals("There should be 3 properties", 3, actualProperties.size()); + + String actualOptions = actualProperties.get("options"); + String actualSource = actualProperties.get("source"); + String actualTarget = actualProperties.get("target"); + + assertNotNull(actualSource); + assertNotNull(actualTarget); + if (expectedSourceSuffix != null) + { + assertTrue("The source file \""+actualSource+"\" should have ended in \""+expectedSourceSuffix+"\"", actualSource.endsWith(expectedSourceSuffix)); + actualSource = actualSource.substring(0, actualSource.length()-expectedSourceSuffix.length()); + } + + assertNotNull(actualOptions); + if (expectedOptions != null) + { + assertEquals("expectedOptions", expectedOptions, actualOptions); + } + + Long actualTimeout = invocation.getArgumentAt(1, Long.class); + assertNotNull(actualTimeout); + if (expectedTimeout != null) + { + assertEquals("expectedTimeout", expectedTimeout, actualTimeout); + } + + // Copy a test file into the target file location if it exists + int i = actualTarget.lastIndexOf('_'); + if (i >= 0) + { + String testFilename = actualTarget.substring(i+1); + File testFile = getTestFile(testFilename, false); + if (testFile != null) + { + File targetFile = new File(actualTarget); + FileChannel source = new FileInputStream(testFile).getChannel(); + FileChannel target = new FileOutputStream(targetFile).getChannel(); + target.transferFrom(source, 0, source.size()); + } + } + + // Check the supplied source file has not been changed. + byte[] actualSourceFileBytes = Files.readAllBytes(new File(actualSource).toPath()); + assertTrue("Source file is not the same", Arrays.equals(expectedSourceFileBytes, actualSourceFileBytes)); + + return mockExecutionResult; + } + }); + + when(mockExecutionResult.getExitValue()).thenReturn(0); + when(mockExecutionResult.getStdErr()).thenReturn("STDERROR"); + when(mockExecutionResult.getStdOut()).thenReturn("STDOUT"); + } + + protected File getTestFile(String testFilename, boolean required) throws IOException + { + ClassLoader classLoader = getClass().getClassLoader(); + URL testFileUrl = classLoader.getResource(testFilename); + if (required && testFileUrl == null) + { + throw new IOException("The test file "+testFilename+" does not exist in the resources directory"); + } + return testFileUrl == null ? null : new File(testFileUrl.getFile()); + } + + @Test + public void simpleTransformTest() throws Exception + { + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension)) + .andExpect(status().is(200)) + .andExpect(content().bytes(expectedTargetFileBytes)) + .andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); + } + + @Test + public void testDelayTest() throws Exception + { + long start = System.currentTimeMillis(); + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension) + .param("testDelay", "400")) + .andExpect(status().is(200)) + .andExpect(content().bytes(expectedTargetFileBytes)) + .andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); + long ms = System.currentTimeMillis()- start; + System.out.println("Transform incluing test delay was "+ms); + assertTrue("Delay sending the result back was too small "+ms, ms >= 400); + assertTrue("Delay sending the result back was too big "+ms, ms <= 500); + } + + @Test + public void noTargetFileTest() throws Exception + { + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", "xxx")) + .andExpect(status().is(500)); + } + + @Test + public void badExitCodeTest() throws Exception + { + when(mockExecutionResult.getExitValue()).thenReturn(1); + + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", "xxx")) + .andExpect(status().is(400)) + .andExpect(status().reason(containsString("Transformer exit code was not 0: \nSTDERR"))); + } + + @Test + // Looks dangerous but is okay as we only use the final filename + public void dotDotSourceFilenameTest() throws Exception + { + sourceFile = new MockMultipartFile("file", "../quick."+sourceExtension, sourceMimetype, expectedSourceFileBytes); + + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension)) + .andExpect(status().is(200)) + .andExpect(content().bytes(expectedTargetFileBytes)) + .andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); + } + + @Test + // Is okay, as the target filename is built up from the whole source filename and the targetExtenstion + public void noExtensionSourceFilenameTest() throws Exception + { + sourceFile = new MockMultipartFile("file", "../quick", sourceMimetype, expectedSourceFileBytes); + + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension)) + .andExpect(status().is(200)) + .andExpect(content().bytes(expectedTargetFileBytes)) + .andExpect(header().string("Content-Disposition", "attachment; filename*= UTF-8''quick."+targetExtension)); + } + + @Test + // Invalid file name that ends in / + public void badSourceFilenameTest() throws Exception + { + sourceFile = new MockMultipartFile("file", "abc/", sourceMimetype, expectedSourceFileBytes); + + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension)) + .andExpect(status().is(400)) + .andExpect(status().reason(containsString("The source filename was not supplied"))); + } + + @Test + public void blankSourceFilenameTest() throws Exception + { + sourceFile = new MockMultipartFile("file", "", sourceMimetype, expectedSourceFileBytes); + + mockMvc.perform(MockMvcRequestBuilders.fileUpload("/transform") + .file(sourceFile) + .param("targetExtension", targetExtension)) + .andExpect(status().is(400)) + .andExpect(status().reason(containsString("The source filename was not supplied"))); + } + +// @Test +// // Not a real test, but helpful for trying out the duration times in log code. +// public void testTimes() throws InterruptedException +// { +// LogEntry.start(); +// Thread.sleep(50); +// LogEntry.setSource("test File", 1234); +// Thread.sleep(200); +// LogEntry.setStatusCodeAndMessage(200, "Success"); +// LogEntry.addDelay(2000L); +// for (LogEntry logEntry: LogEntry.getLog()) +// { +// String str = logEntry.getDuration(); +// System.out.println(str); +// } +// } +} diff --git a/alfresco-transformer-base/src/test/java/org/org/alfresco/transformer/AbstractTransformerControllerTest b/alfresco-transformer-base/src/test/java/org/org/alfresco/transformer/AbstractTransformerControllerTest new file mode 100644 index 00000000..e69de29b diff --git a/pom.xml b/pom.xml index 32f033a5..4b4b40cd 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,6 @@ org.alfresco alfresco-docker-transformers - 0.9-SNAPSHOT pom