brother_mfp: first set of unit tests for decoder.

Looks like we found the rare lost data issue in gray decoding.
Yayy! for unit tests.
brother_mfp_backend
Ralph Little 2022-09-24 20:44:28 -07:00
rodzic 6eb9e1cc43
commit 22d8ab2b49
8 zmienionych plików z 864 dodań i 23 usunięć

Wyświetl plik

@ -400,7 +400,7 @@ libsane_brother_mfp_la_LIBADD = $(COMMON_LIBS) libbrother_mfp.la \
../sanei/sanei_usb.lo \
../sanei/sanei_config.lo \
sane_strstatus.lo \
$(USB_LIBS) $(JPEG_LIBS)
$(USB_LIBS) $(JPEG_LIBS)
EXTRA_DIST += brother_mfp.conf.in

Wyświetl plik

@ -182,6 +182,12 @@ SANE_Status BrotherEncoderFamily4::DecodeSessionResp (const SANE_Byte *data, siz
return SANE_STATUS_GOOD;
}
/*
* NOTE:
* Supply a buffer that is at least 1 char longer than is necessary
* as snprintf() will require space for a terminating NUL.
*
*/
SANE_Status BrotherEncoderFamily4::EncodeBasicParameterBlock (SANE_Byte *data, size_t data_len,
size_t *length)
{
@ -229,15 +235,6 @@ SANE_Status BrotherEncoderFamily4::DecodeBasicParameterBlockResp (const SANE_Byt
SANE_Status BrotherEncoderFamily4::EncodeADFBlock (SANE_Byte *data, size_t data_len, size_t *length)
{
const char *mode_text = ScanModeToText (scan_params.param_scan_mode);
if (nullptr == mode_text)
{
DBG (DBG_SERIOUS,
"BrotherEncoderFamily4::EncodeBasicParameterBlock: failed to get scan mode text for mode %d\n",
scan_params.param_scan_mode);
return SANE_STATUS_INVAL;
}
*length = snprintf ((char*) data, data_len, "\x1b" "D\nADF\n" "\x80");
if (*length > data_len)
@ -289,7 +286,7 @@ SANE_Status BrotherEncoderFamily4::EncodeParameterBlock (SANE_Byte *data, size_t
(unsigned int) scan_params.param_y_res,
mode_text,
(scan_params.param_scan_mode == BROTHER_SCAN_MODE_COLOR) ?
"C=JPEG\nJ=MID\n" : "C=RLENGTH\n",
"C=JPEG\nJ=MID" : "C=RLENGTH",
(unsigned int) (scan_params.param_brightness + 50),
(unsigned int) (scan_params.param_contrast + 50),
(unsigned int) (scan_params.param_pixel_x_offset),
@ -601,7 +598,13 @@ DecodeStatus BrotherGrayRLengthDecoder::DecodeScanData (const SANE_Byte *in_buff
size_t in_buffer_len_start = in_buffer_len;
size_t out_buffer_len_start = out_buffer_len;
while (in_buffer_len && out_buffer_len)
/*
* Notice that we do the check for in and out space at the bottom, not here:
* We might not have any additional src, but we might still be able to progress the decode,
* especially in the case of an in-progress decompression.
*
*/
do
{
/*
* Check the current state to see what we should do.
@ -690,7 +693,7 @@ DecodeStatus BrotherGrayRLengthDecoder::DecodeScanData (const SANE_Byte *in_buff
decode_state = BROTHER_DECODE_RLEN_INIT;
}
}
}
} while (in_buffer_len && out_buffer_len);
if (!in_buffer_len || !out_buffer_len)
{

Wyświetl plik

@ -91,8 +91,8 @@ struct BrotherSessionResponse
struct BrotherParameters
{
BrotherParameters():
param_brightness (50),
param_contrast (50),
param_brightness (0),
param_contrast (0),
param_pixel_x_offset (0),
param_pixel_x_width (0),
param_pixel_y_offset (0),

Wyświetl plik

@ -11,14 +11,19 @@ TEST_LDADD = \
../../../lib/liblib.la \
../../../backend/libbrother_mfp.la \
../../../backend/sane_strstatus.lo \
$(USB_LIBS) $(XML_LIBS) $(PTHREAD_LIBS)
$(USB_LIBS) $(XML_LIBS) $(PTHREAD_LIBS) $(JPEG_LIBS)
check_PROGRAMS = brother_mfp_unit_tests
check_PROGRAMS = brother_mfp_unit_tests
TESTS = brother_mfp_unit_tests
AM_CPPFLAGS += -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include $(USB_CFLAGS) \
-DBACKEND_NAME=brother_mfp -DTESTSUITE_BACKEND_BROTHER_MFP_SRCDIR=$(srcdir)
brother_mfp_unit_tests_SOURCES = brother_mfp_tests.cpp brother_mfp_tests.h
brother_mfp_unit_tests_SOURCES = \
brother_mfp_tests.cpp \
brother_mfp_tests.h \
brother_mfp_tests_family4.cpp \
brother_mfp_tests_gray_rlength.cpp \
../genesys/minigtest.cpp
brother_mfp_unit_tests_LDADD = $(TEST_LDADD)

Wyświetl plik

@ -18,14 +18,17 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#define DEBUG_DECLARE_ONLY
#define DEBUG_NOT_STATIC
#include <stdio.h>
#include "../../include/sane/sanei_debug.h"
#include "../genesys/minigtest.h"
#include "brother_mfp_tests.h"
int main()
int main ()
{
printf("Brother MFP tests.\n");
DBG_INIT ();
return 0;
test_family4 ();
test_gray_rlength ();
return finish_tests ();
}

Wyświetl plik

@ -19,3 +19,6 @@
*/
#pragma once
extern void test_family4();
extern void test_gray_rlength();

Wyświetl plik

@ -0,0 +1,406 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
This file is part of the SANE package.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#define DEBUG_DECLARE_ONLY
#include "brother_mfp_tests.h"
#include "../../backend/brother_mfp/brother_mfp-encoder.h"
#include "../genesys/minigtest.h"
/*
* TEST: DecodeSessionResp()
*
*/
static void test_family4_decode_session_resp()
{
SANE_Status sane_resp;
BrotherSessionResponse sess_resp;
BrotherEncoderFamily4 encoder;
// SUCCESS status
const SANE_Byte *data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x00";
sane_resp = encoder.DecodeSessionResp (data, 5, sess_resp);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
ASSERT_TRUE(sess_resp.ready);
// BUSY status
data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x20";
sane_resp = encoder.DecodeSessionResp (data, 5, sess_resp);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
ASSERT_FALSE(sess_resp.ready);
// Length issues
data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x20" "\x32";
sane_resp = encoder.DecodeSessionResp (data, 6, sess_resp);
ASSERT_EQ(sane_resp, SANE_STATUS_IO_ERROR);
sane_resp = encoder.DecodeSessionResp (data, 4, sess_resp);
ASSERT_EQ(sane_resp, SANE_STATUS_IO_ERROR);
sane_resp = encoder.DecodeSessionResp (data, 0, sess_resp);
ASSERT_EQ(sane_resp, SANE_STATUS_IO_ERROR);
// Content problems.
const char *content_problems[] = {
"\x32" "\x10" "\x01" "\x02" "\x20",
"\x06" "\x32" "\x01" "\x02" "\x20",
"\x06" "\x10" "\x32" "\x02" "\x20",
"\x06" "\x10" "\x01" "\x32" "\x20",
"\x06" "\x10" "\x01" "\x02" "\x32",
nullptr
};
for (size_t test = 0; test; test++)
{
sane_resp = encoder.DecodeSessionResp ((const SANE_Byte *)content_problems[test], 5, sess_resp);
ASSERT_EQ(sane_resp, SANE_STATUS_IO_ERROR);
}
}
/*
* TEST: DecodeBasicParameterBlockResp()
*
*/
static void test_family4_decode_basic_param_resp()
{
/*
* TODO: Nothing to do here yet.
* We don't decode anything from this block.
* Watch this space.
*
*/
}
/*
* TEST: DecodeADFBlockResp()
*
*/
static void test_family4_decode_adf_resp()
{
SANE_Status sane_resp;
BrotherADFResponse adf_resp;
BrotherEncoderFamily4 encoder;
// SUCCESS status
const SANE_Byte *data = (const SANE_Byte *)"\xc2";
sane_resp = encoder.DecodeADFBlockResp (data, 1, adf_resp);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
ASSERT_EQ(adf_resp.resp_code, (SANE_Byte)0xc2);
// Wrong length.
sane_resp = encoder.DecodeADFBlockResp (data, 0, adf_resp);
ASSERT_EQ(sane_resp, SANE_STATUS_IO_ERROR);
ASSERT_EQ(adf_resp.resp_code, (SANE_Byte)0xc2);
sane_resp = encoder.DecodeADFBlockResp (data, 20, adf_resp);
ASSERT_EQ(sane_resp, SANE_STATUS_IO_ERROR);
ASSERT_EQ(adf_resp.resp_code, (SANE_Byte)0xc2);
}
/*
* TEST: EncodeBasicParameterBlock()
*
*/
static void test_family4_encode_basic_param()
{
SANE_Status sane_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily4 encoder;
// All defaults.
sane_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=100,100\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t)22);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different resolutions.
encoder.SetRes(200,300);
sane_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t)22);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// GRAY mode.
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY);
sane_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=GRAY64\n" "\x80";
ASSERT_EQ(ret_length, (size_t )23);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// COLOR mode
encoder.SetScanMode (BROTHER_SCAN_MODE_COLOR);
sane_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=CGRAY\n" "\x80";
ASSERT_EQ(ret_length, (size_t )22);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// GRAY DITHERED mode
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY_DITHERED);
sane_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=ERRDIF\n" "\x80";
ASSERT_EQ(ret_length, (size_t )23);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// TEXT mode
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
sane_resp = encoder.EncodeBasicParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "I\nR=200,300\nM=TEXT\n" "\x80";
ASSERT_EQ(ret_length, (size_t )21);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Buffer too short.
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
sane_resp = encoder.EncodeBasicParameterBlock (data_buffer, 15, &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_INVAL);
}
/*
* TEST: EncodeParameterBlock()
*
*/
static void test_family4_encode_param()
{
SANE_Status sane_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily4 encoder;
// All defaults.
sane_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=100,100\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different resolutions.
encoder.SetRes(200,300);
sane_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Different modes:
// GRAY mode.
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY);
sane_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=GRAY64\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// COLOR mode
encoder.SetScanMode (BROTHER_SCAN_MODE_COLOR);
sane_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=CGRAY\nC=JPEG\nJ=MID\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// GRAY DITHERED mode
encoder.SetScanMode (BROTHER_SCAN_MODE_GRAY_DITHERED);
sane_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=ERRDIF\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// TEXT mode
encoder.SetScanMode (BROTHER_SCAN_MODE_TEXT);
sane_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=50\nN=50\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Different brightness and contrast, positive and negative.
encoder.SetBrightness (-20);
encoder.SetContrast (-30);
sane_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=30\nN=20\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
encoder.SetBrightness (50);
encoder.SetContrast (40);
sane_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,0,0,0\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Different dimensions
encoder.SetScanDimensions (0, 10, 50, 100);
sane_resp = encoder.EncodeParameterBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "X\nR=200,300\nM=TEXT\nC=RLENGTH\n"
"B=100\nN=90\nA=0,50,10,150\nS=NORMAL_SCAN\nP=0\n"
"G=0\nL=0\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp (test_ret, data_buffer, sizeof(test_ret) - 1), 0);
}
// Buffer too short.
sane_resp = encoder.EncodeParameterBlock (data_buffer, 15, &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_INVAL);
}
/*
* TEST: EncodeADFBlock()
*
*/
static void test_family4_encode_adf()
{
SANE_Status sane_resp;
SANE_Byte data_buffer[1024];
size_t ret_length;
BrotherEncoderFamily4 encoder;
// Standard call.
sane_resp = encoder.EncodeADFBlock (data_buffer, sizeof(data_buffer), &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_GOOD);
{
const char test_ret[] = "\x1b" "D\nADF\n" "\x80";
ASSERT_EQ(ret_length, sizeof(test_ret) - 1);
ASSERT_EQ(memcmp(test_ret, data_buffer, sizeof(test_ret)), 0);
}
// Buffer too short.
sane_resp = encoder.EncodeADFBlock (data_buffer, 5, &ret_length);
ASSERT_EQ(sane_resp, SANE_STATUS_INVAL);
}
/*
* Run all the tests.
*
*/
void test_family4 ()
{
// Decodes.
test_family4_decode_session_resp();
test_family4_decode_basic_param_resp();
test_family4_decode_adf_resp();
// Encodes.
test_family4_encode_basic_param();
test_family4_encode_param();
test_family4_encode_adf();
}

Wyświetl plik

@ -0,0 +1,421 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
This file is part of the SANE package.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#define DEBUG_DECLARE_ONLY
#include "../../include/sane/sanei_debug.h"
#include "brother_mfp_tests.h"
#include "../../backend/brother_mfp/brother_mfp-encoder.h"
#include "../genesys/minigtest.h"
/*
* TEST: BrotherGrayRLengthDecoder()
*
*/
static void test_gray_rlength_no_compress()
{
BrotherGrayRLengthDecoder decoder;
const SANE_Byte *src_data;
SANE_Byte dest_data[1024];
DecodeStatus res_code;
size_t src_data_consumed;
size_t dest_data_written;
/*
* Normal decode.
*
*/
src_data = (const SANE_Byte *)"\x04" "ABCDE";
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)6);
ASSERT_EQ(dest_data_written, (size_t)5);
ASSERT_EQ(memcmp(dest_data, "ABCDE", 5), 0);
// Boundary condition: 1 character only.
src_data = (const SANE_Byte *)"\x00" "X";
res_code = decoder.DecodeScanData (src_data,
2,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)1);
ASSERT_EQ(memcmp(dest_data, "X", 1), 0);
/*
* Decodes broken through src exhaustion (fragmentation of input).
*
*/
// Break after length: can decode the length but nothing else.
src_data = (const SANE_Byte *)"\x04";
res_code = decoder.DecodeScanData (src_data,
1,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)1);
ASSERT_EQ(dest_data_written, (size_t)0);
src_data = (const SANE_Byte *)"MNOPQ";
res_code = decoder.DecodeScanData (src_data,
5,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)5);
ASSERT_EQ(dest_data_written, (size_t)5);
ASSERT_EQ(memcmp(dest_data, "MNOPQ", 5), 0);
// BREAK part way through the data portion.
src_data = (const SANE_Byte *)"\x05" "ABC";
res_code = decoder.DecodeScanData (src_data,
4,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)4);
ASSERT_EQ(dest_data_written, (size_t)3);
ASSERT_EQ(memcmp(dest_data, "ABC", 3), 0);
src_data = (const SANE_Byte *)"DEF";
res_code = decoder.DecodeScanData (src_data,
3,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)3);
ASSERT_EQ(dest_data_written, (size_t)3);
ASSERT_EQ(memcmp(dest_data, "DEF", 3), 0);
/*
* Decodes broken by exhausted output buffer.
*
*/
// BREAK part way through the data portion.
src_data = (const SANE_Byte *)"\x05" "MNOPQR";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
7,
&src_data_consumed,
dest_data,
1,
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)1);
ASSERT_EQ(memcmp(dest_data, "M", 1), 0);
src_data = (const SANE_Byte *)"NOPQR";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
5,
&src_data_consumed,
dest_data,
2,
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)2);
ASSERT_EQ(memcmp(dest_data, "NO", 1), 0);
src_data = (const SANE_Byte *)"PQR";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
3,
&src_data_consumed,
dest_data,
3,
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)3);
ASSERT_EQ(dest_data_written, (size_t)3);
ASSERT_EQ(memcmp(dest_data, "PQR", 3), 0);
/*
* Test reset by page.
* This should reset the state of the decoder.
*
*/
src_data = (const SANE_Byte *)"\x04" "ABCDE";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
3,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)3);
ASSERT_EQ(dest_data_written, (size_t)2);
ASSERT_EQ(memcmp(dest_data, "AB", 2), 0);
// Partial decode should be flushed.
decoder.NewPage();
src_data = (const SANE_Byte *)"\x04" "GHIJK";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
6,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)6);
ASSERT_EQ(dest_data_written, (size_t)5);
ASSERT_EQ(memcmp(dest_data, "GHIJK", 5), 0);
/*
* Test reset by block.
* This should reset the state of the decoder.
*
*/
src_data = (const SANE_Byte *)"\x04" "ABCDE";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
3,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)3);
ASSERT_EQ(dest_data_written, (size_t)2);
ASSERT_EQ(memcmp(dest_data, "AB", 2), 0);
// Partial decode should be flushed.
decoder.NewBlock();
src_data = (const SANE_Byte *)"\x04" "GHIJK";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
6,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)6);
ASSERT_EQ(dest_data_written, (size_t)5);
ASSERT_EQ(memcmp(dest_data, "GHIJK", 5), 0);
}
static void test_gray_rlength_compression()
{
BrotherGrayRLengthDecoder decoder;
const SANE_Byte *src_data;
SANE_Byte dest_data[1024];
DecodeStatus res_code;
size_t src_data_consumed;
size_t dest_data_written;
/*
* Normal decode.
*
*/
src_data = (const SANE_Byte *)"\xfd" "A";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)4);
ASSERT_EQ(memcmp(dest_data, "AAAA", 4), 0);
// Boundary check: minimal compression: 2 chars
src_data = (const SANE_Byte *)"\xff" "X";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)2);
ASSERT_EQ(memcmp(dest_data, "XX", 2), 0);
// Upper limit of compression: 0x80 (gives 129 bytes of output)
src_data = (const SANE_Byte *)"\x80" "Y";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)129);
ASSERT_EQ(strrchr((const char *)dest_data, 'Y'), (const char *)&dest_data[128]);
/*
* Fragmentation due to dest exhaustion.
*
*/
src_data = (const SANE_Byte *)"\xfd" "Z";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
strlen((const char *)src_data),
&src_data_consumed,
dest_data,
1,
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)1);
ASSERT_EQ(memcmp(dest_data, "Z", 1), 0);
src_data = (const SANE_Byte *)"\xfd" "Z";
memset(dest_data, 0, sizeof(dest_data));
/*
* Can decode, but we haven't any additional src data.
* This is a special case of decompression where we do not require
* additional input from the src; just some output space.
*
*/
res_code = decoder.DecodeScanData (src_data,
0,
&src_data_consumed,
dest_data,
3,
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)0);
ASSERT_EQ(dest_data_written, (size_t)3);
ASSERT_EQ(memcmp(dest_data, "ZZZ", 3), 0);
/*
* Fragmentation due to src exhaustion.
* The only case here is that we see the length, but not the
* following character so we cannot actually do anything.
*
*/
src_data = (const SANE_Byte *)"\xfd" "A";
memset(dest_data, 0, sizeof(dest_data));
res_code = decoder.DecodeScanData (src_data,
1,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)0);
ASSERT_EQ(dest_data_written, (size_t)0);
res_code = decoder.DecodeScanData (src_data,
2,
&src_data_consumed,
dest_data,
sizeof(dest_data),
&dest_data_written);
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
ASSERT_EQ(src_data_consumed, (size_t)2);
ASSERT_EQ(dest_data_written, (size_t)4);
ASSERT_EQ(memcmp(dest_data, "AAAA", 4), 0);
}
/*
* Run all the tests.
*
*/
void test_gray_rlength()
{
test_gray_rlength_no_compress();
test_gray_rlength_compression();
}