AbstractMimeEntityTest.php 33KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111
  1. <?php
  2. require_once 'Swift/Mime/MimeEntity.php';
  3. require_once 'Swift/Tests/SwiftUnitTestCase.php';
  4. require_once 'Swift/Mime/ContentEncoder.php';
  5. require_once 'Swift/Mime/Header.php';
  6. require_once 'Swift/Mime/ParameterizedHeader.php';
  7. require_once 'Swift/KeyCache.php';
  8. require_once 'Swift/Mime/HeaderSet.php';
  9. abstract class Swift_Mime_AbstractMimeEntityTest
  10. extends Swift_Tests_SwiftUnitTestCase
  11. {
  12. public function testGetHeadersReturnsHeaderSet()
  13. {
  14. $headers = $this->_createHeaderSet();
  15. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  16. $this->_createCache()
  17. );
  18. $this->assertSame($headers, $entity->getHeaders());
  19. }
  20. public function testContentTypeIsReturnedFromHeader()
  21. {
  22. $ctype = $this->_createHeader('Content-Type', 'image/jpeg-test');
  23. $headers = $this->_createHeaderSet(array('Content-Type' => $ctype));
  24. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  25. $this->_createCache()
  26. );
  27. $this->assertEqual('image/jpeg-test', $entity->getContentType());
  28. }
  29. public function testContentTypeIsSetInHeader()
  30. {
  31. $ctype = $this->_createHeader('Content-Type', 'text/plain', array(), false);
  32. $headers = $this->_createHeaderSet(array('Content-Type' => $ctype));
  33. $this->_checking(Expectations::create()
  34. -> one($ctype)->setFieldBodyModel('image/jpeg')
  35. -> ignoring($ctype)
  36. );
  37. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  38. $this->_createCache()
  39. );
  40. $entity->setContentType('image/jpeg');
  41. }
  42. public function testContentTypeHeaderIsAddedIfNoneSet()
  43. {
  44. $headers = $this->_createHeaderSet(array(), false);
  45. $this->_checking(Expectations::create()
  46. -> one($headers)->addParameterizedHeader('Content-Type', 'image/jpeg')
  47. -> ignoring($headers)
  48. );
  49. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  50. $this->_createCache()
  51. );
  52. $entity->setContentType('image/jpeg');
  53. }
  54. public function testContentTypeCanBeSetViaSetBody()
  55. {
  56. $headers = $this->_createHeaderSet(array(), false);
  57. $this->_checking(Expectations::create()
  58. -> one($headers)->addParameterizedHeader('Content-Type', 'text/html')
  59. -> ignoring($headers)
  60. );
  61. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  62. $this->_createCache()
  63. );
  64. $entity->setBody('<b>foo</b>', 'text/html');
  65. }
  66. public function testGetEncoderFromConstructor()
  67. {
  68. $encoder = $this->_createEncoder('base64');
  69. $entity = $this->_createEntity($this->_createHeaderSet(), $encoder,
  70. $this->_createCache()
  71. );
  72. $this->assertSame($encoder, $entity->getEncoder());
  73. }
  74. public function testSetAndGetEncoder()
  75. {
  76. $encoder = $this->_createEncoder('base64');
  77. $headers = $this->_createHeaderSet();
  78. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  79. $this->_createCache()
  80. );
  81. $entity->setEncoder($encoder);
  82. $this->assertSame($encoder, $entity->getEncoder());
  83. }
  84. public function testSettingEncoderUpdatesTransferEncoding()
  85. {
  86. $encoder = $this->_createEncoder('base64');
  87. $encoding = $this->_createHeader(
  88. 'Content-Transfer-Encoding', '8bit', array(), false
  89. );
  90. $headers = $this->_createHeaderSet(array(
  91. 'Content-Transfer-Encoding' => $encoding
  92. ));
  93. $this->_checking(Expectations::create()
  94. -> one($encoding)->setFieldBodyModel('base64')
  95. -> ignoring($encoding)
  96. );
  97. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  98. $this->_createCache()
  99. );
  100. $entity->setEncoder($encoder);
  101. }
  102. public function testSettingEncoderAddsEncodingHeaderIfNonePresent()
  103. {
  104. $headers = $this->_createHeaderSet(array(), false);
  105. $this->_checking(Expectations::create()
  106. -> one($headers)->addTextHeader('Content-Transfer-Encoding', 'something')
  107. -> ignoring($headers)
  108. );
  109. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  110. $this->_createCache()
  111. );
  112. $entity->setEncoder($this->_createEncoder('something'));
  113. }
  114. public function testIdIsReturnedFromHeader()
  115. {
  116. /* -- RFC 2045, 7.
  117. In constructing a high-level user agent, it may be desirable to allow
  118. one body to make reference to another. Accordingly, bodies may be
  119. labelled using the "Content-ID" header field, which is syntactically
  120. identical to the "Message-ID" header field
  121. */
  122. $cid = $this->_createHeader('Content-ID', 'zip@button');
  123. $headers = $this->_createHeaderSet(array('Content-ID' => $cid));
  124. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  125. $this->_createCache()
  126. );
  127. $this->assertEqual('zip@button', $entity->getId());
  128. }
  129. public function testIdIsSetInHeader()
  130. {
  131. $cid = $this->_createHeader('Content-ID', 'zip@button', array(), false);
  132. $headers = $this->_createHeaderSet(array('Content-ID' => $cid));
  133. $this->_checking(Expectations::create()
  134. -> one($cid)->setFieldBodyModel('foo@bar')
  135. -> ignoring($cid)
  136. );
  137. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  138. $this->_createCache()
  139. );
  140. $entity->setId('foo@bar');
  141. }
  142. public function testIdIsAutoGenerated()
  143. {
  144. $entity = $this->_createEntity($this->_createHeaderSet(),
  145. $this->_createEncoder(), $this->_createCache()
  146. );
  147. $this->assertPattern('/^.*?@.*?$/D', $entity->getId());
  148. }
  149. public function testGenerateIdCreatesNewId()
  150. {
  151. $headers = $this->_createHeaderSet();
  152. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  153. $this->_createCache()
  154. );
  155. $id1 = $entity->generateId();
  156. $id2 = $entity->generateId();
  157. $this->assertNotEqual($id1, $id2);
  158. }
  159. public function testGenerateIdSetsNewId()
  160. {
  161. $headers = $this->_createHeaderSet();
  162. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  163. $this->_createCache()
  164. );
  165. $id = $entity->generateId();
  166. $this->assertEqual($id, $entity->getId());
  167. }
  168. public function testDescriptionIsReadFromHeader()
  169. {
  170. /* -- RFC 2045, 8.
  171. The ability to associate some descriptive information with a given
  172. body is often desirable. For example, it may be useful to mark an
  173. "image" body as "a picture of the Space Shuttle Endeavor." Such text
  174. may be placed in the Content-Description header field. This header
  175. field is always optional.
  176. */
  177. $desc = $this->_createHeader('Content-Description', 'something');
  178. $headers = $this->_createHeaderSet(array('Content-Description' => $desc));
  179. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  180. $this->_createCache()
  181. );
  182. $this->assertEqual('something', $entity->getDescription());
  183. }
  184. public function testDescriptionIsSetInHeader()
  185. {
  186. $desc = $this->_createHeader('Content-Description', '', array(), false);
  187. $headers = $this->_createHeaderSet(array('Content-Description' => $desc));
  188. $this->_checking(Expectations::create()
  189. -> one($desc)->setFieldBodyModel('whatever')
  190. -> ignoring($desc)
  191. );
  192. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  193. $this->_createCache()
  194. );
  195. $entity->setDescription('whatever');
  196. }
  197. public function testDescriptionHeaderIsAddedIfNotPresent()
  198. {
  199. $headers = $this->_createHeaderSet(array(), false);
  200. $this->_checking(Expectations::create()
  201. -> one($headers)->addTextHeader('Content-Description', 'whatever')
  202. -> ignoring($headers)
  203. );
  204. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  205. $this->_createCache()
  206. );
  207. $entity->setDescription('whatever');
  208. }
  209. public function testSetAndGetMaxLineLength()
  210. {
  211. $entity = $this->_createEntity($this->_createHeaderSet(),
  212. $this->_createEncoder(), $this->_createCache()
  213. );
  214. $entity->setMaxLineLength(60);
  215. $this->assertEqual(60, $entity->getMaxLineLength());
  216. }
  217. public function testEncoderIsUsedForStringGeneration()
  218. {
  219. $encoder = $this->_createEncoder('base64', false);
  220. $this->_checking(Expectations::create()
  221. -> one($encoder)->encodeString('blah', optional())
  222. -> ignoring($encoder)
  223. );
  224. $entity = $this->_createEntity($this->_createHeaderSet(),
  225. $encoder, $this->_createCache()
  226. );
  227. $entity->setBody("blah");
  228. $entity->toString();
  229. }
  230. public function testMaxLineLengthIsProvidedWhenEncoding()
  231. {
  232. $encoder = $this->_createEncoder('base64', false);
  233. $this->_checking(Expectations::create()
  234. -> one($encoder)->encodeString('blah', 0, 65)
  235. -> ignoring($encoder)
  236. );
  237. $entity = $this->_createEntity($this->_createHeaderSet(),
  238. $encoder, $this->_createCache()
  239. );
  240. $entity->setBody("blah");
  241. $entity->setMaxLineLength(65);
  242. $entity->toString();
  243. }
  244. public function testHeadersAppearInString()
  245. {
  246. $headers = $this->_createHeaderSet(array(), false);
  247. $this->_checking(Expectations::create()
  248. -> ignoring($headers)->toString() -> returns(
  249. "Content-Type: text/plain; charset=utf-8\r\n" .
  250. "X-MyHeader: foobar\r\n"
  251. )
  252. -> ignoring($headers)
  253. );
  254. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  255. $this->_createCache()
  256. );
  257. $this->assertEqual(
  258. "Content-Type: text/plain; charset=utf-8\r\n" .
  259. "X-MyHeader: foobar\r\n",
  260. $entity->toString()
  261. );
  262. }
  263. public function testSetAndGetBody()
  264. {
  265. $entity = $this->_createEntity($this->_createHeaderSet(),
  266. $this->_createEncoder(), $this->_createCache()
  267. );
  268. $entity->setBody("blah\r\nblah!");
  269. $this->assertEqual("blah\r\nblah!", $entity->getBody());
  270. }
  271. public function testBodyIsAppended()
  272. {
  273. $headers = $this->_createHeaderSet(array(), false);
  274. $this->_checking(Expectations::create()
  275. -> ignoring($headers)->toString() -> returns(
  276. "Content-Type: text/plain; charset=utf-8\r\n"
  277. )
  278. -> ignoring($headers)
  279. );
  280. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  281. $this->_createCache()
  282. );
  283. $entity->setBody("blah\r\nblah!");
  284. $this->assertEqual(
  285. "Content-Type: text/plain; charset=utf-8\r\n" .
  286. "\r\n" .
  287. "blah\r\nblah!",
  288. $entity->toString()
  289. );
  290. }
  291. public function testGetBodyReturnsStringFromByteStream()
  292. {
  293. $os = $this->_createOutputStream("byte stream string");
  294. $entity = $this->_createEntity($this->_createHeaderSet(),
  295. $this->_createEncoder(), $this->_createCache()
  296. );
  297. $entity->setBody($os);
  298. $this->assertEqual("byte stream string", $entity->getBody());
  299. }
  300. public function testByteStreamBodyIsAppended()
  301. {
  302. $headers = $this->_createHeaderSet(array(), false);
  303. $os = $this->_createOutputStream("streamed");
  304. $this->_checking(Expectations::create()
  305. -> ignoring($headers)->toString() -> returns(
  306. "Content-Type: text/plain; charset=utf-8\r\n"
  307. )
  308. -> ignoring($headers)
  309. );
  310. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  311. $this->_createCache()
  312. );
  313. $entity->setBody($os);
  314. $this->assertEqual(
  315. "Content-Type: text/plain; charset=utf-8\r\n" .
  316. "\r\n" .
  317. "streamed",
  318. $entity->toString()
  319. );
  320. }
  321. public function testBoundaryCanBeRetrieved()
  322. {
  323. /* -- RFC 2046, 5.1.1.
  324. boundary := 0*69<bchars> bcharsnospace
  325. bchars := bcharsnospace / " "
  326. bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
  327. "+" / "_" / "," / "-" / "." /
  328. "/" / ":" / "=" / "?"
  329. */
  330. $entity = $this->_createEntity($this->_createHeaderSet(),
  331. $this->_createEncoder(), $this->_createCache()
  332. );
  333. $this->assertPattern(
  334. '/^[a-zA-Z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-zA-Z0-9\'\(\)\+_\-,\.\/:=\?]$/D',
  335. $entity->getBoundary()
  336. );
  337. }
  338. public function testBoundaryNeverChanges()
  339. {
  340. $entity = $this->_createEntity($this->_createHeaderSet(),
  341. $this->_createEncoder(), $this->_createCache()
  342. );
  343. $firstBoundary = $entity->getBoundary();
  344. for ($i = 0; $i < 10; $i++)
  345. {
  346. $this->assertEqual($firstBoundary, $entity->getBoundary());
  347. }
  348. }
  349. public function testBoundaryCanBeSet()
  350. {
  351. $entity = $this->_createEntity($this->_createHeaderSet(),
  352. $this->_createEncoder(), $this->_createCache()
  353. );
  354. $entity->setBoundary('foobar');
  355. $this->assertEqual('foobar', $entity->getBoundary());
  356. }
  357. public function testAddingChildrenGeneratesBoundaryInHeaders()
  358. {
  359. $child = $this->_createChild();
  360. $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false);
  361. $this->_checking(Expectations::create()
  362. -> one($cType)->setParameter('boundary', any())
  363. -> ignoring($cType)
  364. );
  365. $entity = $this->_createEntity($this->_createHeaderSet(array(
  366. 'Content-Type' => $cType
  367. )),
  368. $this->_createEncoder(), $this->_createCache()
  369. );
  370. $entity->setChildren(array($child));
  371. }
  372. public function testChildrenOfLevelAttachmentAndLessCauseMultipartMixed()
  373. {
  374. for ($level = Swift_Mime_MimeEntity::LEVEL_MIXED;
  375. $level > Swift_Mime_MimeEntity::LEVEL_TOP; $level /= 2)
  376. {
  377. $child = $this->_createChild($level);
  378. $cType = $this->_createHeader(
  379. 'Content-Type', 'text/plain', array(), false
  380. );
  381. $this->_checking(Expectations::create()
  382. -> one($cType)->setFieldBodyModel('multipart/mixed')
  383. -> ignoring($cType)
  384. );
  385. $entity = $this->_createEntity($this->_createHeaderSet(array(
  386. 'Content-Type' => $cType)),
  387. $this->_createEncoder(), $this->_createCache()
  388. );
  389. $entity->setChildren(array($child));
  390. }
  391. }
  392. public function testChildrenOfLevelAlternativeAndLessCauseMultipartAlternative()
  393. {
  394. for ($level = Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE;
  395. $level > Swift_Mime_MimeEntity::LEVEL_MIXED; $level /= 2)
  396. {
  397. $child = $this->_createChild($level);
  398. $cType = $this->_createHeader(
  399. 'Content-Type', 'text/plain', array(), false
  400. );
  401. $this->_checking(Expectations::create()
  402. -> one($cType)->setFieldBodyModel('multipart/alternative')
  403. -> ignoring($cType)
  404. );
  405. $entity = $this->_createEntity($this->_createHeaderSet(array(
  406. 'Content-Type' => $cType)),
  407. $this->_createEncoder(), $this->_createCache()
  408. );
  409. $entity->setChildren(array($child));
  410. }
  411. }
  412. public function testChildrenOfLevelRelatedAndLessCauseMultipartRelated()
  413. {
  414. for ($level = Swift_Mime_MimeEntity::LEVEL_RELATED;
  415. $level > Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE; $level /= 2)
  416. {
  417. $child = $this->_createChild($level);
  418. $cType = $this->_createHeader(
  419. 'Content-Type', 'text/plain', array(), false
  420. );
  421. $this->_checking(Expectations::create()
  422. -> one($cType)->setFieldBodyModel('multipart/related')
  423. -> ignoring($cType)
  424. );
  425. $entity = $this->_createEntity($this->_createHeaderSet(array(
  426. 'Content-Type' => $cType)),
  427. $this->_createEncoder(), $this->_createCache()
  428. );
  429. $entity->setChildren(array($child));
  430. }
  431. }
  432. public function testHighestLevelChildDeterminesContentType()
  433. {
  434. $combinations = array(
  435. array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED,
  436. Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE,
  437. Swift_Mime_MimeEntity::LEVEL_RELATED
  438. ),
  439. 'type' => 'multipart/mixed'
  440. ),
  441. array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED,
  442. Swift_Mime_MimeEntity::LEVEL_RELATED
  443. ),
  444. 'type' => 'multipart/mixed'
  445. ),
  446. array('levels' => array(Swift_Mime_MimeEntity::LEVEL_MIXED,
  447. Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE
  448. ),
  449. 'type' => 'multipart/mixed'
  450. ),
  451. array('levels' => array(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE,
  452. Swift_Mime_MimeEntity::LEVEL_RELATED
  453. ),
  454. 'type' => 'multipart/alternative'
  455. )
  456. );
  457. foreach ($combinations as $combination)
  458. {
  459. $children = array();
  460. foreach ($combination['levels'] as $level)
  461. {
  462. $children[] = $this->_createChild($level);
  463. }
  464. $cType = $this->_createHeader(
  465. 'Content-Type', 'text/plain', array(), false
  466. );
  467. $this->_checking(Expectations::create()
  468. -> one($cType)->setFieldBodyModel($combination['type'])
  469. -> ignoring($cType)
  470. );
  471. $entity = $this->_createEntity($this->_createHeaderSet(array(
  472. 'Content-Type' => $cType)),
  473. $this->_createEncoder(), $this->_createCache()
  474. );
  475. $entity->setChildren($children);
  476. }
  477. }
  478. public function testChildrenAppearNestedInString()
  479. {
  480. /* -- RFC 2046, 5.1.1.
  481. (excerpt too verbose to paste here)
  482. */
  483. $headers = $this->_createHeaderSet(array(), false);
  484. $child1 = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE,
  485. "Content-Type: text/plain\r\n" .
  486. "\r\n" .
  487. "foobar"
  488. );
  489. $child2 = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE,
  490. "Content-Type: text/html\r\n" .
  491. "\r\n" .
  492. "<b>foobar</b>"
  493. );
  494. $this->_checking(Expectations::create()
  495. -> ignoring($headers)->toString() -> returns(
  496. "Content-Type: multipart/alternative; boundary=\"xxx\"\r\n"
  497. )
  498. -> ignoring($headers)
  499. );
  500. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  501. $this->_createCache()
  502. );
  503. $entity->setBoundary('xxx');
  504. $entity->setChildren(array($child1, $child2));
  505. $this->assertEqual(
  506. "Content-Type: multipart/alternative; boundary=\"xxx\"\r\n" .
  507. "\r\n" .
  508. "\r\n--xxx\r\n" .
  509. "Content-Type: text/plain\r\n" .
  510. "\r\n" .
  511. "foobar\r\n" .
  512. "\r\n--xxx\r\n" .
  513. "Content-Type: text/html\r\n" .
  514. "\r\n" .
  515. "<b>foobar</b>\r\n" .
  516. "\r\n--xxx--\r\n",
  517. $entity->toString()
  518. );
  519. }
  520. public function testMixingLevelsIsHierarchical()
  521. {
  522. $headers = $this->_createHeaderSet(array(), false);
  523. $newHeaders = $this->_createHeaderSet(array(), false);
  524. $part = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE,
  525. "Content-Type: text/plain\r\n" .
  526. "\r\n" .
  527. "foobar"
  528. );
  529. $attachment = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_MIXED,
  530. "Content-Type: application/octet-stream\r\n" .
  531. "\r\n" .
  532. "data"
  533. );
  534. $this->_checking(Expectations::create()
  535. -> ignoring($headers)->toString() -> returns(
  536. "Content-Type: multipart/mixed; boundary=\"xxx\"\r\n"
  537. )
  538. -> ignoring($headers)->newInstance() -> returns($newHeaders)
  539. -> ignoring($headers)
  540. -> ignoring($newHeaders)->toString() -> returns(
  541. "Content-Type: multipart/alternative; boundary=\"yyy\"\r\n"
  542. )
  543. -> ignoring($newHeaders)
  544. );
  545. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  546. $this->_createCache()
  547. );
  548. $entity->setBoundary('xxx');
  549. $entity->setChildren(array($part, $attachment));
  550. $this->assertPattern(
  551. "~^" .
  552. "Content-Type: multipart/mixed; boundary=\"xxx\"\r\n" .
  553. "\r\n\r\n--xxx\r\n" .
  554. "Content-Type: multipart/alternative; boundary=\"yyy\"\r\n" .
  555. "\r\n\r\n--(.*?)\r\n" .
  556. "Content-Type: text/plain\r\n" .
  557. "\r\n" .
  558. "foobar" .
  559. "\r\n\r\n--\\1--\r\n" .
  560. "\r\n\r\n--xxx\r\n" .
  561. "Content-Type: application/octet-stream\r\n" .
  562. "\r\n" .
  563. "data" .
  564. "\r\n\r\n--xxx--\r\n" .
  565. "\$~",
  566. $entity->toString()
  567. );
  568. }
  569. public function testSettingEncoderNotifiesChildren()
  570. {
  571. $child = $this->_createChild(0, '', false);
  572. $encoder = $this->_createEncoder('base64');
  573. $this->_checking(Expectations::create()
  574. -> one($child)->encoderChanged($encoder)
  575. -> ignoring($child)
  576. );
  577. $entity = $this->_createEntity($this->_createHeaderSet(),
  578. $this->_createEncoder(), $this->_createCache()
  579. );
  580. $entity->setChildren(array($child));
  581. $entity->setEncoder($encoder);
  582. }
  583. public function testReceiptOfEncoderChangeNotifiesChildren()
  584. {
  585. $child = $this->_createChild(0, '', false);
  586. $encoder = $this->_createEncoder('base64');
  587. $this->_checking(Expectations::create()
  588. -> one($child)->encoderChanged($encoder)
  589. -> ignoring($child)
  590. );
  591. $entity = $this->_createEntity($this->_createHeaderSet(),
  592. $this->_createEncoder(), $this->_createCache()
  593. );
  594. $entity->setChildren(array($child));
  595. $entity->encoderChanged($encoder);
  596. }
  597. public function testReceiptOfCharsetChangeNotifiesChildren()
  598. {
  599. $child = $this->_createChild(0, '', false);
  600. $this->_checking(Expectations::create()
  601. -> one($child)->charsetChanged('windows-874')
  602. -> ignoring($child)
  603. );
  604. $entity = $this->_createEntity($this->_createHeaderSet(),
  605. $this->_createEncoder(), $this->_createCache()
  606. );
  607. $entity->setChildren(array($child));
  608. $entity->charsetChanged('windows-874');
  609. }
  610. public function testEntityIsWrittenToByteStream()
  611. {
  612. $entity = $this->_createEntity($this->_createHeaderSet(),
  613. $this->_createEncoder(), $this->_createCache()
  614. );
  615. $is = $this->_createInputStream(false);
  616. $this->_checking(Expectations::create()
  617. -> atLeast(1)->of($is)->write(any())
  618. -> ignoring($is)
  619. );
  620. $entity->toByteStream($is);
  621. }
  622. public function testEntityHeadersAreComittedToByteStream()
  623. {
  624. $entity = $this->_createEntity($this->_createHeaderSet(),
  625. $this->_createEncoder(), $this->_createCache()
  626. );
  627. $is = $this->_createInputStream(false);
  628. $this->_checking(Expectations::create()
  629. -> atLeast(1)->of($is)->commit()
  630. -> atLeast(1)->of($is)->write(any())
  631. -> ignoring($is)
  632. );
  633. $entity->toByteStream($is);
  634. }
  635. public function testOrderingTextBeforeHtml()
  636. {
  637. $htmlChild = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE,
  638. "Content-Type: text/html\r\n" .
  639. "\r\n" .
  640. "HTML PART",
  641. false
  642. );
  643. $textChild = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE,
  644. "Content-Type: text/plain\r\n" .
  645. "\r\n" .
  646. "TEXT PART",
  647. false
  648. );
  649. $headers = $this->_createHeaderSet(array(), false);
  650. $this->_checking(Expectations::create()
  651. -> ignoring($headers)->toString() -> returns(
  652. "Content-Type: multipart/alternative; boundary=\"xxx\"\r\n"
  653. )
  654. -> ignoring($headers)
  655. -> ignoring($htmlChild)->getContentType() -> returns('text/html')
  656. -> ignoring($htmlChild)
  657. -> ignoring($textChild)->getContentType() -> returns('text/plain')
  658. -> ignoring($textChild)
  659. );
  660. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  661. $this->_createCache()
  662. );
  663. $entity->setBoundary('xxx');
  664. $entity->setChildren(array($htmlChild, $textChild));
  665. $this->assertEqual(
  666. "Content-Type: multipart/alternative; boundary=\"xxx\"\r\n" .
  667. "\r\n\r\n--xxx\r\n" .
  668. "Content-Type: text/plain\r\n" .
  669. "\r\n" .
  670. "TEXT PART" .
  671. "\r\n\r\n--xxx\r\n" .
  672. "Content-Type: text/html\r\n" .
  673. "\r\n" .
  674. "HTML PART" .
  675. "\r\n\r\n--xxx--\r\n",
  676. $entity->toString()
  677. );
  678. }
  679. public function testUnsettingChildrenRestoresContentType()
  680. {
  681. $cType = $this->_createHeader('Content-Type', 'text/plain', array(), false);
  682. $child = $this->_createChild(Swift_Mime_MimeEntity::LEVEL_ALTERNATIVE);
  683. $s = $this->_mockery()->sequence('Type setting');
  684. $this->_checking(Expectations::create()
  685. -> one($cType)->setFieldBodyModel('image/jpeg') -> inSequence($s)
  686. -> one($cType)->setFieldBodyModel('multipart/alternative') -> inSequence($s)
  687. -> one($cType)->setFieldBodyModel('image/jpeg') -> inSequence($s)
  688. -> ignoring($cType)
  689. );
  690. $entity = $this->_createEntity($this->_createHeaderSet(array(
  691. 'Content-Type' => $cType
  692. )),
  693. $this->_createEncoder(), $this->_createCache()
  694. );
  695. $entity->setContentType('image/jpeg');
  696. $entity->setChildren(array($child));
  697. $entity->setChildren(array());
  698. }
  699. public function testBodyIsReadFromCacheWhenUsingToStringIfPresent()
  700. {
  701. $headers = $this->_createHeaderSet(array(), false);
  702. $this->_checking(Expectations::create()
  703. -> ignoring($headers)->toString() -> returns(
  704. "Content-Type: text/plain; charset=utf-8\r\n"
  705. )
  706. -> ignoring($headers)
  707. );
  708. $cache = $this->_createCache(false);
  709. $this->_checking(Expectations::create()
  710. -> one($cache)->hasKey(any(), 'body') -> returns(true)
  711. -> one($cache)->getString(any(), 'body') -> returns("\r\ncache\r\ncache!")
  712. -> ignoring($cache)
  713. );
  714. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  715. $cache
  716. );
  717. $entity->setBody("blah\r\nblah!");
  718. $this->assertEqual(
  719. "Content-Type: text/plain; charset=utf-8\r\n" .
  720. "\r\n" .
  721. "cache\r\ncache!",
  722. $entity->toString()
  723. );
  724. }
  725. public function testBodyIsAddedToCacheWhenUsingToString()
  726. {
  727. $headers = $this->_createHeaderSet(array(), false);
  728. $this->_checking(Expectations::create()
  729. -> ignoring($headers)->toString() -> returns(
  730. "Content-Type: text/plain; charset=utf-8\r\n"
  731. )
  732. -> ignoring($headers)
  733. );
  734. $cache = $this->_createCache(false);
  735. $this->_checking(Expectations::create()
  736. -> one($cache)->hasKey(any(), 'body') -> returns(false)
  737. -> one($cache)->setString(any(), 'body', "\r\nblah\r\nblah!", Swift_KeyCache::MODE_WRITE)
  738. -> ignoring($cache)
  739. );
  740. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  741. $cache
  742. );
  743. $entity->setBody("blah\r\nblah!");
  744. $entity->toString();
  745. }
  746. public function testBodyIsClearedFromCacheIfNewBodySet()
  747. {
  748. $headers = $this->_createHeaderSet(array(), false);
  749. $this->_checking(Expectations::create()
  750. -> ignoring($headers)->toString() -> returns(
  751. "Content-Type: text/plain; charset=utf-8\r\n"
  752. )
  753. -> ignoring($headers)
  754. );
  755. $cache = $this->_createCache(false);
  756. $this->_checking(Expectations::create()
  757. -> one($cache)->clearKey(any(), 'body')
  758. -> ignoring($cache)
  759. );
  760. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  761. $cache
  762. );
  763. $entity->setBody("blah\r\nblah!");
  764. $entity->toString();
  765. $entity->setBody("new\r\nnew!");
  766. }
  767. public function testBodyIsNotClearedFromCacheIfSameBodySet()
  768. {
  769. $headers = $this->_createHeaderSet(array(), false);
  770. $this->_checking(Expectations::create()
  771. -> ignoring($headers)->toString() -> returns(
  772. "Content-Type: text/plain; charset=utf-8\r\n"
  773. )
  774. -> ignoring($headers)
  775. );
  776. $cache = $this->_createCache(false);
  777. $this->_checking(Expectations::create()
  778. -> never($cache)->clearKey(any(), 'body')
  779. -> ignoring($cache)
  780. );
  781. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  782. $cache
  783. );
  784. $entity->setBody("blah\r\nblah!");
  785. $entity->toString();
  786. $entity->setBody("blah\r\nblah!");
  787. }
  788. public function testBodyIsClearedFromCacheIfNewEncoderSet()
  789. {
  790. $headers = $this->_createHeaderSet(array(), false);
  791. $this->_checking(Expectations::create()
  792. -> ignoring($headers)->toString() -> returns(
  793. "Content-Type: text/plain; charset=utf-8\r\n"
  794. )
  795. -> ignoring($headers)
  796. );
  797. $cache = $this->_createCache(false);
  798. $this->_checking(Expectations::create()
  799. -> one($cache)->clearKey(any(), 'body')
  800. -> ignoring($cache)
  801. );
  802. $otherEncoder = $this->_createEncoder();
  803. $entity = $this->_createEntity($headers, $this->_createEncoder(),
  804. $cache
  805. );
  806. $entity->setBody("blah\r\nblah!");
  807. $entity->toString();
  808. $entity->setEncoder($otherEncoder);
  809. }
  810. public function testBodyIsReadFromCacheWhenUsingToByteStreamIfPresent()
  811. {
  812. $is = $this->_createInputStream();
  813. $cache = $this->_createCache(false);
  814. $this->_checking(Expectations::create()
  815. -> one($cache)->hasKey(any(), 'body') -> returns(true)
  816. -> one($cache)->exportToByteStream(any(), 'body', $is)
  817. -> ignoring($cache)
  818. );
  819. $entity = $this->_createEntity($this->_createHeaderSet(),
  820. $this->_createEncoder(), $cache
  821. );
  822. $entity->setBody('foo');
  823. $entity->toByteStream($is);
  824. }
  825. public function testBodyIsAddedToCacheWhenUsingToByteStream()
  826. {
  827. $is = $this->_createInputStream();
  828. $cache = $this->_createCache(false);
  829. $this->_checking(Expectations::create()
  830. -> one($cache)->hasKey(any(), 'body') -> returns(false)
  831. //The input stream should be fetched for writing
  832. // Proving that it's actually written to is possible, but extremely
  833. // fragile. Best let the acceptance tests cover this aspect
  834. -> one($cache)->getInputByteStream(any(), 'body')
  835. -> ignoring($cache)
  836. );
  837. $entity = $this->_createEntity($this->_createHeaderSet(),
  838. $this->_createEncoder(), $cache
  839. );
  840. $entity->setBody('foo');
  841. $entity->toByteStream($is);
  842. }
  843. public function testFluidInterface()
  844. {
  845. $entity = $this->_createEntity($this->_createHeaderSet(),
  846. $this->_createEncoder(), $this->_createCache()
  847. );
  848. $this->assertSame($entity,
  849. $entity
  850. ->setContentType('text/plain')
  851. ->setEncoder($this->_createEncoder())
  852. ->setId('foo@bar')
  853. ->setDescription('my description')
  854. ->setMaxLineLength(998)
  855. ->setBody('xx')
  856. ->setBoundary('xyz')
  857. ->setChildren(array())
  858. );
  859. }
  860. // -- Private helpers
  861. abstract protected function _createEntity($headers, $encoder, $cache);
  862. protected function _createChild($level = null, $string = '', $stub = true)
  863. {
  864. $child = $this->_mock('Swift_Mime_MimeEntity');
  865. if (isset($level))
  866. {
  867. $this->_checking(Expectations::create()
  868. -> ignoring($child)->getNestingLevel() -> returns($level)
  869. );
  870. }
  871. $this->_checking(Expectations::create()
  872. -> ignoring($child)->toString() -> returns($string)
  873. );
  874. if ($stub)
  875. {
  876. $this->_checking(Expectations::create()
  877. -> ignoring($child)
  878. );
  879. }
  880. return $child;
  881. }
  882. protected function _createEncoder($name = 'quoted-printable', $stub = true)
  883. {
  884. $encoder = $this->_mock('Swift_Mime_ContentEncoder');
  885. $this->_checking(Expectations::create()
  886. -> ignoring($encoder)->getName() -> returns($name)
  887. );
  888. if ($stub)
  889. {
  890. $this->_checking(Expectations::create()
  891. -> ignoring($encoder)->encodeString(any(), optional())
  892. -> calls(array($this, 'returnStringFromEncoder'))
  893. -> ignoring($encoder)
  894. );
  895. }
  896. return $encoder;
  897. }
  898. protected function _createCache($stub = true)
  899. {
  900. $cache = $this->_mock('Swift_KeyCache');
  901. if ($stub)
  902. {
  903. $this->_checking(Expectations::create()
  904. -> ignoring($cache)
  905. );
  906. }
  907. return $cache;
  908. }
  909. protected function _createHeaderSet($headers = array(), $stub = true)
  910. {
  911. $set = $this->_mock('Swift_Mime_HeaderSet');
  912. foreach ($headers as $key => $header)
  913. {
  914. $this->_checking(Expectations::create()
  915. -> ignoring($set)->has($key) -> returns(true)
  916. -> ignoring($set)->get($key) -> returns($header)
  917. );
  918. }
  919. if ($stub)
  920. {
  921. $this->_checking(Expectations::create()
  922. -> ignoring($set)->newInstance() -> returns($set)
  923. -> ignoring($set)
  924. );
  925. }
  926. return $set;
  927. }
  928. protected function _createHeader($name, $model = null, $params = array(), $stub = true)
  929. {
  930. $header = $this->_mock('Swift_Mime_ParameterizedHeader');
  931. $this->_checking(Expectations::create()
  932. -> ignoring($header)->getFieldName() -> returns($name)
  933. -> ignoring($header)->getFieldBodyModel() -> returns($model)
  934. );
  935. foreach ($params as $key => $value)
  936. {
  937. $this->_checking(Expectations::create()
  938. -> ignoring($header)->getParameter($key) -> returns($value)
  939. );
  940. }
  941. if ($stub)
  942. {
  943. $this->_checking(Expectations::create()
  944. -> ignoring($header)
  945. );
  946. }
  947. return $header;
  948. }
  949. protected function _createOutputStream($data = null, $stub = true)
  950. {
  951. $os = $this->_mock('Swift_OutputByteStream');
  952. if (isset($data))
  953. {
  954. $pos = $this->_mockery()->states('position')->startsAs('at beginning');
  955. $this->_checking(Expectations::create()
  956. -> ignoring($os)->read(optional()) -> returns($data)
  957. -> when($pos->isNot('at end')) -> then($pos->is('at end'))
  958. -> ignoring($os)->read(optional()) -> returns(false)
  959. );
  960. if ($stub)
  961. {
  962. $this->_checking(Expectations::create()
  963. -> ignoring($os)
  964. );
  965. }
  966. }
  967. return $os;
  968. }
  969. protected function _createInputStream($stub = true)
  970. {
  971. $is = $this->_mock('Swift_InputByteStream');
  972. if ($stub)
  973. {
  974. $this->_checking(Expectations::create()
  975. -> ignoring($is)
  976. );
  977. }
  978. return $is;
  979. }
  980. // -- Mock helpers
  981. public function returnStringFromEncoder(Yay_Invocation $invocation)
  982. {
  983. $args = $invocation->getArguments();
  984. return array_shift($args);
  985. }
  986. }