blob: 5fdb8c46594674e3837f2e71bb64b7b92c0c87ab [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "DFPlatform.h"
#include "BDTTests.h"
#include "DFDOM.h"
#include "DFBDT.h"
#include "DFString.h"
#include "DFXML.h"
#include "DFCommon.h"
#include "DFUnitTest.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// ItemLens //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct ItemLens ItemLens;
struct ItemLens {
DFDocument *abstractDoc;
DFDocument *concreteDoc;
};
static ItemLens *ItemLensNew(DFDocument *abstractDoc, DFDocument *concreteDoc)
{
ItemLens *lens = (ItemLens *)xcalloc(1,sizeof(ItemLens));
lens->abstractDoc = DFDocumentRetain(abstractDoc);
lens->concreteDoc = DFDocumentRetain(concreteDoc);
return lens;
}
static void ItemLensFree(ItemLens *lens)
{
DFDocumentRelease(lens->abstractDoc);
DFDocumentRelease(lens->concreteDoc);
free(lens);
}
static DFNode *ItemGet(ItemLens *lens, DFNode *concrete)
{
switch (concrete->tag) {
case HTML_P: {
DFNode *li = DFCreateElement(lens->abstractDoc,HTML_H1);
DFFormatAttribute(li,HTML_ID,"x%u", concrete->seqNo);
char *value = DFNodeTextToString(concrete);
DFNode *text = DFCreateTextNode(lens->abstractDoc,value);
free(value);
DFAppendChild(li,text);
return li;
}
default:
return NULL;
}
}
static int ItemIsVisible(ItemLens *lens, DFNode *concrete)
{
switch (concrete->tag) {
case HTML_P:
return 1;
default:
return 0;
}
}
static DFNode *ItemToConcrete(ItemLens *lens, DFNode *abstract)
{
DFNode *result = NULL;
const char *idval = DFGetAttribute(abstract,HTML_ID);
if ((idval != NULL) && DFStringHasPrefix(idval,"x")) {
unsigned int seqNo = atoi(&idval[1]);
result = DFNodeForSeqNo(lens->concreteDoc,seqNo);
assert(result != NULL);
}
if (result == NULL) {
// FIXME: Not covered by tests
DFNode *p = DFCreateElement(lens->concreteDoc,HTML_P);
char *value = DFNodeTextToString(abstract);
DFNode *text = DFCreateTextNode(lens->concreteDoc,value);
free(value);
DFFormatAttribute(abstract,HTML_ID,"x%u",p->seqNo);
DFAppendChild(p,text);
result = p;
}
return result;
}
DFLens lensItem = {
.isVisible = (void *)ItemIsVisible,
.get = (void *)ItemGet,
.put = NULL,
.create = NULL,
.remove = NULL,
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// Test code //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct TestContainerLens TestContainerLens;
struct TestContainerLens {
DFDocument *abstractDoc;
DFDocument *concreteDoc;
ItemLens *itemLens;
};
static TestContainerLens *TestContainerLensNew(DFDocument *abstractDoc, DFDocument *concreteDoc)
{
TestContainerLens *lens = (TestContainerLens *)xcalloc(1,sizeof(TestContainerLens));
lens->abstractDoc = DFDocumentRetain(abstractDoc);
lens->concreteDoc = DFDocumentRetain(concreteDoc);
lens->itemLens = ItemLensNew(abstractDoc,concreteDoc);
return lens;
}
static void TestContainerLensFree(TestContainerLens *lens)
{
DFDocumentRelease(lens->abstractDoc);
DFDocumentRelease(lens->concreteDoc);
ItemLensFree(lens->itemLens);
free(lens);
}
DFNode *TestContainerLensToAbstract(TestContainerLens *lens, DFNode *concrete)
{
DFNode *abstract = DFCreateElement(lens->abstractDoc,HTML_UL);
BDTContainerGet(lens->itemLens,&lensItem,abstract,concrete);
return abstract;
}
void TestContainerLensPut(TestContainerLens *lens, DFNode *abstract, DFNode *concrete)
{
BDTContainerPut(lens->itemLens,&lensItem,abstract,concrete,
(DFLookupConcreteFunction)ItemToConcrete);
}
void TestContainerLensRemove(TestContainerLens *lens, DFNode *concrete)
{
for (DFNode *child = concrete->first; child != NULL; child = child->next)
lensItem.remove(lens->itemLens,child);
}
static DFDocument *createHTMLDoc()
{
DFDocument *doc = DFDocumentNew();
DFNode *html = DFCreateElement(doc,HTML_HTML);
DFNode *body = DFCreateElement(doc,HTML_BODY);
DFAppendChild(doc->docNode,html);
DFAppendChild(html,body);
return doc;
}
static DFDocument *createConcrete()
{
const char *strings[] = {
"start1",
"start2",
"a",
"b",
"mid1",
"mid2",
"c",
"d",
"mid3",
"mid4",
"e",
"f",
"end1",
"end2",
NULL };
DFDocument *doc = createHTMLDoc();
for (int i = 0; strings[i]; i++) {
const char *str = strings[i];
DFNode *item;
if (strlen(str) == 1)
item = DFCreateElement(doc,HTML_P);
else
item = DFCreateElement(doc,HTML_DIV);
DFNode *body = doc->docNode->last->last;
DFNode *text = DFCreateTextNode(doc,str);
DFAppendChild(body,item);
DFAppendChild(item,text);
}
return doc;
}
static void move(DFNode *container, int count, int from, int to)
{
if ((to >= from) && (to <= from+count))
return;;
DFNode *nextSibling = NULL;
DFArray *move = DFArrayNew(NULL,NULL);
int index = 0;
for (DFNode *child = container->first; child != NULL; child = child->next) {
if (index == to)
nextSibling = child;
if ((index >= from) && (index < from + count))
DFArrayAppend(move,child);
index++;
}
for (size_t i = 0; i < DFArrayCount(move); i++) {
DFNode *child = DFArrayItemAt(move,i);
DFInsertBefore(container,child,nextSibling);
}
DFArrayRelease(move);
}
static void removeChildren(DFNode *container, int *indices, int count)
{
int index = 0;
DFNode *next;
for (DFNode *child = container->first; child != NULL; child = next) {
next = child->next;
int removeThisNode = 0;
for (int i = 0; i < count; i++) {
if (indices[i] == index)
removeThisNode = 1;
}
if (removeThisNode)
DFRemoveNode(child);
index++;
}
}
static DFDocument *getAbstractDoc(DFDocument *concreteDoc)
{
DFDocument *abstractDoc = DFDocumentNew();
DFNode *html = DFCreateElement(abstractDoc,HTML_HTML);
DFAppendChild(abstractDoc->docNode,html);
DFNode *concrete = concreteDoc->docNode->last->last;
TestContainerLens *containerLens = TestContainerLensNew(abstractDoc,concreteDoc);
DFNode *abstract = TestContainerLensToAbstract(containerLens,concrete);
DFAppendChild(html,abstract);
TestContainerLensFree(containerLens);
return abstractDoc;
}
static int charIsNewline(int c)
{
return (c == '\n');
}
static void combinedListing(DFDocument *abstractDoc, DFDocument *concreteDoc, DFBuffer *output)
{
DFInsertBefore(abstractDoc->docNode,DFCreateTextNode(abstractDoc,""),abstractDoc->docNode->first);
DFInsertBefore(concreteDoc->docNode,DFCreateTextNode(concreteDoc,""),concreteDoc->docNode->first);
char *abstractStr = DFSerializeXMLString(abstractDoc,0,1);
char *concreteStr = DFSerializeXMLString(concreteDoc,0,1);
const char **abstractLines = DFStringTokenize(abstractStr,charIsNewline);
const char **concreteLines = DFStringTokenize(concreteStr,charIsNewline);
size_t abstractCount = DFStringArrayCount(abstractLines);
size_t concreteCount = DFStringArrayCount(concreteLines);
size_t absIndex = 0;
size_t conIndex = 0;
while ((absIndex < abstractCount) && (conIndex < concreteCount)) {
const char *absLine = abstractLines[absIndex];
const char *conLine = concreteLines[conIndex];
if (DFStringHasSuffix(conLine,"</div>")) {
DFBufferFormat(output,"%-30s %-40s\n","",conLine);
conIndex++;
}
else {
DFBufferFormat(output,"%-30s %-40s\n",absLine,conLine);
absIndex++;
conIndex++;
}
}
while (absIndex < abstractCount) {
DFBufferFormat(output,"%s\n",abstractLines[absIndex]);
absIndex++;
}
while (conIndex < concreteCount) {
DFBufferFormat(output,"%-30s %s\n","",concreteLines[conIndex]);
conIndex++;
}
free(abstractStr);
free(concreteStr);
free(abstractLines);
free(concreteLines);
DFBufferFormat(output,"%-30s %-40s\n","",""); // to satisfy tests
}
static void BDT_testResult(DFDocument *concreteDoc, DFDocument *abstractDoc, DFBuffer *output)
{
char *oldAbstractStr = DFSerializeXMLString(abstractDoc,0,1);
TestContainerLens *containerLens = TestContainerLensNew(abstractDoc,concreteDoc);
TestContainerLensPut(containerLens,abstractDoc->docNode->first->first,concreteDoc->docNode->first->first);
TestContainerLensFree(containerLens);
DFDocument *newAbstractDoc = getAbstractDoc(concreteDoc);
char *newAbstractStr = DFSerializeXMLString(newAbstractDoc,0,1);
DFDocumentRelease(newAbstractDoc);
DFStripIds(abstractDoc->docNode);
combinedListing(abstractDoc,concreteDoc,output);
int match = !strcmp(newAbstractStr,oldAbstractStr);
DFBufferFormat(output,"Match? %s\n",match ? "true" : "false");
free(oldAbstractStr);
free(newAbstractStr);
}
void BDT_testMove(int count, int from, int to, DFBuffer *output)
{
DFDocument *concreteDoc = createConcrete();
DFDocument *abstractDoc = getAbstractDoc(concreteDoc);
move(abstractDoc->docNode->first->first,count,from,to);
BDT_testResult(concreteDoc,abstractDoc,output);
DFDocumentRelease(concreteDoc);
DFDocumentRelease(abstractDoc);
}
void BDT_testRemove(int *indices, int count, DFBuffer *output)
{
DFDocument *concreteDoc = createConcrete();
DFDocument *abstractDoc = getAbstractDoc(concreteDoc);
removeChildren(abstractDoc->docNode->first->first,indices,count);
BDT_testResult(concreteDoc,abstractDoc,output);
DFDocumentRelease(concreteDoc);
DFDocumentRelease(abstractDoc);
}
int BDT_Test(int argc, const char **argv)
{
if (argc < 3) {
printf("Usage: dfutil -bdt count from to\n");
return 1;
}
int count = atoi(argv[0]);
int from = atoi(argv[1]);
int to = atoi(argv[2]);
DFBuffer *output = DFBufferNew();
BDT_testMove(count,from,to,output);
printf("%s",output->data);
DFBufferRelease(output);
return 0;
}
static void test_move(void)
{
if (utgetargc() < 3) {
DFBufferFormat(utgetoutput(),"move: insufficient arguments");
return;
}
int count = atoi(utgetargv()[0]);
int from = atoi(utgetargv()[1]);
int to = atoi(utgetargv()[2]);
DFBuffer *output = DFBufferNew();
BDT_testMove(count,from,to,output);
DFBufferFormat(utgetoutput(),"%s",output->data);
DFBufferRelease(output);
}
static void test_remove(void)
{
int *indices = (int *)xmalloc(utgetargc()*sizeof(int));
for (int i = 0; i < utgetargc(); i++) {
int index = atoi(utgetargv()[i]);
indices[i] = index;
}
DFBuffer *output = DFBufferNew();
BDT_testRemove(indices,utgetargc(),output);
DFBufferFormat(utgetoutput(),"%s",output->data);
DFBufferRelease(output);
free(indices);
}
TestGroup BDTTests = {
"core.bdt", {
{ "move", DataTest, test_move },
{ "remove", DataTest, test_remove },
{ NULL, PlainTest, NULL }
}
};