1// TranslatorTestAddOn.cpp
2
3#include <stdio.h>
4#include <string.h>
5#include <Translator.h>
6#include <OS.h>
7#include "TranslatorTestAddOn.h"
8
9// ##### Include headers for your tests here #####
10#include "bmptranslator/BMPTranslatorTest.h"
11#include "pngtranslator/PNGTranslatorTest.h"
12#include "stxttranslator/STXTTranslatorTest.h"
13#include "tgatranslator/TGATranslatorTest.h"
14#include "tifftranslator/TIFFTranslatorTest.h"
15
16BTestSuite *
17getTestSuite()
18{
19	BTestSuite *suite = new BTestSuite("Translators");
20
21	// ##### Add test suites here #####
22	suite->addTest("BMPTranslator", BMPTranslatorTest::Suite());
23	suite->addTest("PNGTranslator", PNGTranslatorTest::Suite());
24	suite->addTest("STXTTranslator", STXTTranslatorTest::Suite());
25	suite->addTest("TGATranslator", TGATranslatorTest::Suite());
26	suite->addTest("TIFFTranslator", TIFFTranslatorTest::Suite());
27
28	return suite;
29}
30
31// helper function used by multiple tests to
32// determine if the given streams are exactly
33// the same
34bool
35CompareStreams(BPositionIO &a, BPositionIO &b)
36{
37	off_t alen = 0, blen = 0;
38	const uint32 kbuflen = 2048;
39	uint8 abuf[kbuflen], bbuf[kbuflen];
40
41	a.Seek(0, SEEK_END);
42	alen = a.Position();
43	b.Seek(0, SEEK_END);
44	blen = b.Position();
45	if (alen != blen)
46		return false;
47
48	if (a.Seek(0, SEEK_SET) != 0)
49		return false;
50	if (b.Seek(0, SEEK_SET) != 0)
51		return false;
52
53	ssize_t read = 0;
54	while (alen > 0) {
55		read = a.Read(abuf, kbuflen);
56		if (read < 0)
57			return false;
58		if (b.Read(bbuf, read) != read)
59			return false;
60
61		if (memcmp(abuf, bbuf, read) != 0)
62			return false;
63
64		alen -= read;
65	}
66
67	return true;
68}
69
70// Check each member of translator_info to see that it matches
71// what is expected
72void
73CheckTranslatorInfo(translator_info *pti, uint32 type, uint32 group,
74	float quality, float capability, const char *name, const char *mime)
75{
76	CPPUNIT_ASSERT(pti->type == type);
77	CPPUNIT_ASSERT(pti->translator != 0);
78	CPPUNIT_ASSERT(pti->group == group);
79	CPPUNIT_ASSERT(pti->quality > quality - 0.01f &&
80		pti->quality < quality + 0.01f);
81	CPPUNIT_ASSERT(pti->capability > capability - 0.01f &&
82		pti->capability < capability + 0.01f);
83	CPPUNIT_ASSERT(strcmp(pti->name, name) == 0);
84	CPPUNIT_ASSERT(strcmp(pti->MIME, mime) == 0);
85}
86
87// Returns true if the translation_formats are
88// identical (or nearly identical).  Returns false if
89// they are different
90bool
91CompareTranslationFormat(const translation_format *pleft,
92	const translation_format *pright)
93{
94	CPPUNIT_ASSERT(pleft->MIME);
95	CPPUNIT_ASSERT(pright->MIME);
96	CPPUNIT_ASSERT(pleft->name);
97	CPPUNIT_ASSERT(pright->name);
98
99	if (pleft->group != pright->group)
100		return false;
101	if (pleft->type != pright->type)
102		return false;
103	if (pleft->quality < pright->quality - 0.01f ||
104		pleft->quality > pright->quality + 0.01f)
105		return false;
106	if (pleft->capability < pright->capability - 0.01f ||
107		pleft->capability > pright->capability + 0.01f)
108		return false;
109	if (strcmp(pleft->MIME, pright->MIME) != 0)
110		return false;
111	if (strcmp(pleft->name, pright->name) != 0)
112		return false;
113
114	return true;
115}
116
117// Apply a number of tests to a BTranslator * to a TGATranslator object
118void
119TestBTranslator(BTestCase *ptest, BTranslator *ptran,
120	const translation_format *pExpectedIns, uint32 nExpectedIns,
121	const translation_format *pExpectedOuts, uint32 nExpectedOuts,
122	int32 expectedVer)
123{
124	const uint32 knmatches = 50;
125	uint8 matches[knmatches];
126	CPPUNIT_ASSERT(nExpectedIns <= knmatches && nExpectedOuts <= knmatches);
127
128	// The translator should only have one reference
129	ptest->NextSubTest();
130	CPPUNIT_ASSERT(ptran->ReferenceCount() == 1);
131
132	// Make sure Acquire returns a BTranslator even though its
133	// already been Acquired once
134	ptest->NextSubTest();
135	CPPUNIT_ASSERT(ptran->Acquire() == ptran);
136
137	// Acquired twice, refcount should be 2
138	ptest->NextSubTest();
139	CPPUNIT_ASSERT(ptran->ReferenceCount() == 2);
140
141	// Release should return ptran because it is still acquired
142	ptest->NextSubTest();
143	CPPUNIT_ASSERT(ptran->Release() == ptran);
144
145	ptest->NextSubTest();
146	CPPUNIT_ASSERT(ptran->ReferenceCount() == 1);
147
148	ptest->NextSubTest();
149	CPPUNIT_ASSERT(ptran->Acquire() == ptran);
150
151	ptest->NextSubTest();
152	CPPUNIT_ASSERT(ptran->ReferenceCount() == 2);
153
154	ptest->NextSubTest();
155	CPPUNIT_ASSERT(ptran->Release() == ptran);
156
157	ptest->NextSubTest();
158	CPPUNIT_ASSERT(ptran->ReferenceCount() == 1);
159
160	// A name would be nice
161	ptest->NextSubTest();
162	const char *tranname = ptran->TranslatorName();
163	CPPUNIT_ASSERT(tranname);
164	printf(" {%s} ", tranname);
165
166	// More info would be nice
167	ptest->NextSubTest();
168	const char *traninfo = ptran->TranslatorInfo();
169	CPPUNIT_ASSERT(traninfo);
170	printf(" {%s} ", traninfo);
171
172	// What version are you?
173	// (when ver == 100, that means that version is 1.00)
174	ptest->NextSubTest();
175	int32 ver = ptran->TranslatorVersion();
176	CPPUNIT_ASSERT(ver == expectedVer);
177	printf(" {0x%.8lx} ", ver);
178
179	// Input formats?
180	ptest->NextSubTest();
181	{
182		printf("input:");
183
184		int32 incount = 0;
185		const translation_format *pins = ptran->InputFormats(&incount);
186		CPPUNIT_ASSERT((unsigned)incount == nExpectedIns);
187		CPPUNIT_ASSERT(pins);
188
189		memset(matches, 0, sizeof(uint8) * nExpectedIns);
190		for (int32 i = 0; i < incount; i++) {
191			bool bmatch = false;
192			for (uint32 k = 0; bmatch == false && k < nExpectedIns; k++) {
193				bmatch = CompareTranslationFormat(pins + i, pExpectedIns + k);
194				if (bmatch)
195					matches[k] = 1;
196			}
197
198			CPPUNIT_ASSERT(bmatch);
199		}
200
201		// make sure that each expected input format was matched
202		for (uint32 i = 0; i < nExpectedIns; i++)
203			CPPUNIT_ASSERT(matches[i]);
204	}
205
206	// Output formats?
207	ptest->NextSubTest();
208	{
209		printf("output:");
210
211		int32 outcount = 0;
212		const translation_format *pouts = ptran->OutputFormats(&outcount);
213		CPPUNIT_ASSERT((unsigned)outcount == nExpectedOuts);
214		CPPUNIT_ASSERT(pouts);
215
216		memset(matches, 0, sizeof(uint8) * nExpectedOuts);
217		for (int32 i = 0; i < outcount; i++) {
218			bool bmatch = false;
219			for (uint32 k = 0; bmatch == false && k < nExpectedOuts; k++) {
220				bmatch = CompareTranslationFormat(pouts + i, pExpectedOuts + k);
221				if (bmatch)
222					matches[k] = 1;
223			}
224
225			CPPUNIT_ASSERT(bmatch);
226		}
227
228		// make sure that each expected input format was matched
229		for (uint32 i = 0; i < nExpectedOuts; i++)
230			CPPUNIT_ASSERT(matches[i]);
231	}
232
233	// Release should return NULL because Release has been called
234	// as many times as it has been acquired
235	ptest->NextSubTest();
236	CPPUNIT_ASSERT(ptran->Release() == NULL);
237	ptran = NULL;
238}
239
240void
241TranslatorLoadAddOnTest(const char *path, BTestCase *ptest,
242	const translation_format *pExpectedIns, uint32 nExpectedIns,
243	const translation_format *pExpectedOuts, uint32 nExpectedOuts,
244	int32 expectedVer)
245{
246	// Make sure the add_on loads
247	ptest->NextSubTest();
248	image_id image = load_add_on(path);
249	CPPUNIT_ASSERT(image >= 0);
250
251	// Load in function to make the object
252	ptest->NextSubTest();
253	BTranslator *(*pMakeNthTranslator)(int32 n,image_id you,uint32 flags,...);
254	status_t err = get_image_symbol(image, "make_nth_translator",
255		B_SYMBOL_TYPE_TEXT, (void **)&pMakeNthTranslator);
256	CPPUNIT_ASSERT(!err);
257
258	// Make sure the function returns a pointer to a BTranslator
259	ptest->NextSubTest();
260	BTranslator *ptran = pMakeNthTranslator(0, image, 0);
261	CPPUNIT_ASSERT(ptran);
262
263	// Make sure the function only returns one BTranslator
264	ptest->NextSubTest();
265	CPPUNIT_ASSERT(!pMakeNthTranslator(1, image, 0));
266	CPPUNIT_ASSERT(!pMakeNthTranslator(2, image, 0));
267	CPPUNIT_ASSERT(!pMakeNthTranslator(3, image, 0));
268	CPPUNIT_ASSERT(!pMakeNthTranslator(16, image, 0));
269	CPPUNIT_ASSERT(!pMakeNthTranslator(1023, image, 0));
270
271	// Run a number of tests on the BTranslator object
272	TestBTranslator(ptest, ptran, pExpectedIns, nExpectedIns,
273		pExpectedOuts, nExpectedOuts, expectedVer);
274		// NOTE: this function Release()s ptran
275	ptran = NULL;
276
277	// Unload Add-on
278	ptest->NextSubTest();
279	CPPUNIT_ASSERT(unload_add_on(image) == B_OK);
280}
281