blob: eebe1ed939544792bd2da5e1cd8e53889e9ac977 [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 <string.h>
#define CFC_USE_TEST_MACROS
#include "CFCBase.h"
#include "CFCClass.h"
#include "CFCMethod.h"
#include "CFCParamList.h"
#include "CFCParcel.h"
#include "CFCParser.h"
#include "CFCSymbol.h"
#include "CFCTest.h"
#include "CFCType.h"
#include "CFCUtil.h"
#ifndef true
#define true 1
#define false 0
#endif
static void
S_run_tests(CFCTest *test);
static void
S_run_basic_tests(CFCTest *test);
static void
S_run_parser_tests(CFCTest *test);
static void
S_run_overridden_tests(CFCTest *test);
static void
S_run_final_tests(CFCTest *test);
const CFCTestBatch CFCTEST_BATCH_METHOD = {
"Clownfish::CFC::Model::Method",
76,
S_run_tests
};
static void
S_run_tests(CFCTest *test) {
S_run_basic_tests(test);
S_run_parser_tests(test);
S_run_overridden_tests(test);
S_run_final_tests(test);
}
static char*
S_try_new_method(const char *name, CFCType *return_type,
CFCParamList *param_list, CFCClass *klass) {
char *error;
CFCUTIL_TRY {
CFCMethod *method = CFCMethod_new(NULL, name, return_type, param_list,
NULL, klass, 0, 0);
CFCBase_decref((CFCBase*)method);
}
CFCUTIL_CATCH(error);
return error;
}
static void
S_run_basic_tests(CFCTest *test) {
CFCParser *parser = CFCParser_new();
CFCParcel *neato_parcel
= CFCTest_parse_parcel(test, parser, "parcel Neato;");
CFCClass *neato_foo
= CFCClass_create(neato_parcel, NULL, "Neato::Foo", NULL, NULL, NULL,
NULL, false, false, false);
CFCClass *neato_bar
= CFCClass_create(neato_parcel, NULL, "Neato::Bar", NULL, NULL, NULL,
NULL, false, false, false);
CFCType *return_type = CFCTest_parse_type(test, parser, "Obj*");
CFCParamList *param_list
= CFCTest_parse_param_list(test, parser,
"(Foo *self, int32_t count = 0)");
CFCMethod *method
= CFCMethod_new(NULL, "Return_An_Obj", return_type, param_list, NULL,
neato_foo, 0, 0);
OK(test, method != NULL, "new");
OK(test, CFCSymbol_parcel((CFCSymbol*)method),
"parcel exposure by default");
{
char *error = S_try_new_method("return_an_obj", return_type,
param_list, neato_foo);
OK(test, error && strstr(error, "name"),
"invalid name kills constructor");
FREEMEM(error);
}
{
CFCMethod *dupe
= CFCMethod_new(NULL, "Return_An_Obj", return_type, param_list,
NULL, neato_foo, 0, 0);
OK(test, CFCMethod_compatible(method, dupe), "compatible");
CFCBase_decref((CFCBase*)dupe);
}
{
CFCMethod *name_differs
= CFCMethod_new(NULL, "Eat", return_type, param_list, NULL,
neato_foo, 0, 0);
OK(test, !CFCMethod_compatible(method, name_differs),
"different name spoils compatible");
OK(test, !CFCMethod_compatible(name_differs, method),
"... reversed");
CFCBase_decref((CFCBase*)name_differs);
}
{
static const char *param_strings[5] = {
"(Foo *self, int32_t count = 0, int b)",
"(Foo *self, int32_t count = 1)",
"(Foo *self, int32_t count)",
"(Foo *self, int32_t countess = 0)",
"(Foo *self, uint32_t count = 0)"
};
static const char *test_names[5] = {
"extra param",
"different initial_value",
"missing initial_value",
"different param name",
"different param type"
};
for (int i = 0; i < 5; ++i) {
CFCParamList *other_param_list
= CFCTest_parse_param_list(test, parser, param_strings[i]);
CFCMethod *other
= CFCMethod_new(NULL, "Return_An_Obj", return_type,
other_param_list, NULL, neato_foo, 0, 0);
OK(test, !CFCMethod_compatible(method, other),
"%s spoils compatible", test_names[i]);
OK(test, !CFCMethod_compatible(other, method),
"... reversed");
CFCBase_decref((CFCBase*)other_param_list);
CFCBase_decref((CFCBase*)other);
}
}
{
CFCParamList *self_differs_list
= CFCTest_parse_param_list(test, parser,
"(Bar *self, int32_t count = 0)");
CFCMethod *self_differs
= CFCMethod_new(NULL, "Return_An_Obj", return_type,
self_differs_list, NULL, neato_bar, 0, 0);
OK(test, CFCMethod_compatible(method, self_differs),
"different self type still compatible(),"
" since can't test inheritance");
OK(test, CFCMethod_compatible(self_differs, method),
"... reversed");
CFCBase_decref((CFCBase*)self_differs_list);
CFCBase_decref((CFCBase*)self_differs);
}
{
CFCMethod *aliased
= CFCMethod_new(NULL, "Aliased", return_type, param_list, NULL,
neato_foo, 0, 0);
OK(test, !CFCMethod_get_host_alias(aliased),
"no host alias by default");
CFCMethod_set_host_alias(aliased, "Host_Alias");
STR_EQ(test, CFCMethod_get_host_alias(aliased), "Host_Alias",
"set/get host alias");
CFCBase_decref((CFCBase*)aliased);
}
{
CFCMethod *excluded
= CFCMethod_new(NULL, "Excluded", return_type, param_list, NULL,
neato_foo, 0, 0);
OK(test, !CFCMethod_excluded_from_host(excluded),
"not excluded by default");
CFCMethod_exclude_from_host(excluded);
OK(test, CFCMethod_excluded_from_host(excluded), "exclude from host");
CFCBase_decref((CFCBase*)excluded);
}
CFCBase_decref((CFCBase*)parser);
CFCBase_decref((CFCBase*)neato_parcel);
CFCBase_decref((CFCBase*)neato_foo);
CFCBase_decref((CFCBase*)neato_bar);
CFCBase_decref((CFCBase*)return_type);
CFCBase_decref((CFCBase*)param_list);
CFCBase_decref((CFCBase*)method);
CFCParcel_reap_singletons();
}
static void
S_run_parser_tests(CFCTest *test) {
CFCParser *parser = CFCParser_new();
CFCParcel *neato_parcel
= CFCTest_parse_parcel(test, parser, "parcel Neato;");
CFCClass *neato_obj
= CFCClass_create(neato_parcel, NULL, "Neato::Obj", NULL, NULL, NULL,
NULL, false, false, false);
CFCParser_set_class(parser, neato_obj);
{
static const char *method_strings[4] = {
"public int Do_Foo(Obj *self);",
"Obj* Gimme_An_Obj(Obj *self);",
"void Do_Whatever(Obj *self, uint32_t a_num, float real);",
"Foo* Fetch_Foo(Obj *self, int num);",
};
for (int i = 0; i < 4; ++i) {
CFCMethod *method
= CFCTest_parse_method(test, parser, method_strings[i]);
CFCBase_decref((CFCBase*)method);
}
}
{
CFCMethod *method
= CFCTest_parse_method(test, parser,
"public final void The_End(Obj *self);");
OK(test, CFCMethod_final(method), "final");
CFCBase_decref((CFCBase*)method);
}
CFCBase_decref((CFCBase*)neato_obj);
CFCBase_decref((CFCBase*)neato_parcel);
CFCBase_decref((CFCBase*)parser);
CFCParcel_reap_singletons();
}
static void
S_run_overridden_tests(CFCTest *test) {
CFCParser *parser = CFCParser_new();
CFCParcel *neato_parcel
= CFCTest_parse_parcel(test, parser, "parcel Neato;");
CFCClass *neato_foo
= CFCClass_create(neato_parcel, NULL, "Neato::Foo", NULL, NULL, NULL,
NULL, false, false, false);
CFCClass *neato_foo_jr
= CFCClass_create(neato_parcel, NULL, "Neato::Foo::FooJr", NULL, NULL,
NULL, "Neato::Foo", false, false, false);
CFCType *return_type = CFCTest_parse_type(test, parser, "Obj*");
CFCParamList *param_list
= CFCTest_parse_param_list(test, parser, "(Foo *self)");
CFCMethod *orig
= CFCMethod_new(NULL, "Return_An_Obj", return_type, param_list, NULL,
neato_foo, 0, 0);
CFCParamList *overrider_param_list
= CFCTest_parse_param_list(test, parser, "(FooJr *self)");
CFCMethod *overrider
= CFCMethod_new(NULL, "Return_An_Obj", return_type,
overrider_param_list, NULL, neato_foo_jr, 0, 0);
CFCMethod_override(overrider, orig);
OK(test, !CFCMethod_novel(overrider),
"A Method which overrides another is not 'novel'");
CFCBase_decref((CFCBase*)parser);
CFCBase_decref((CFCBase*)neato_parcel);
CFCBase_decref((CFCBase*)neato_foo);
CFCBase_decref((CFCBase*)neato_foo_jr);
CFCBase_decref((CFCBase*)return_type);
CFCBase_decref((CFCBase*)param_list);
CFCBase_decref((CFCBase*)orig);
CFCBase_decref((CFCBase*)overrider_param_list);
CFCBase_decref((CFCBase*)overrider);
CFCParcel_reap_singletons();
}
static void
S_run_final_tests(CFCTest *test) {
CFCParser *parser = CFCParser_new();
CFCParcel *neato_parcel
= CFCTest_parse_parcel(test, parser, "parcel Neato;");
CFCClass *obj_class
= CFCTest_parse_class(test, parser, "class Obj {}");
CFCClass *foo_class
= CFCTest_parse_class(test, parser, "class Neato::Foo {}");
CFCType *return_type = CFCTest_parse_type(test, parser, "Obj*");
CFCParamList *param_list
= CFCTest_parse_param_list(test, parser, "(Foo *self)");
CFCMethod *not_final
= CFCMethod_new(NULL, "Return_An_Obj", return_type, param_list, NULL,
foo_class, 0, 0);
CFCMethod_resolve_types(not_final);
CFCMethod *final = CFCMethod_finalize(not_final);
OK(test, CFCMethod_compatible(not_final, final),
"finalize clones properly");
OK(test, !CFCMethod_final(not_final), "not final by default");
OK(test, CFCMethod_final(final), "finalize");
{
char *error;
CFCUTIL_TRY {
CFCMethod_override(not_final, final);
}
CFCUTIL_CATCH(error);
OK(test, error && strstr(error, "final"),
"Can't override final method");
FREEMEM(error);
}
CFCBase_decref((CFCBase*)parser);
CFCBase_decref((CFCBase*)neato_parcel);
CFCBase_decref((CFCBase*)obj_class);
CFCBase_decref((CFCBase*)foo_class);
CFCBase_decref((CFCBase*)return_type);
CFCBase_decref((CFCBase*)param_list);
CFCBase_decref((CFCBase*)not_final);
CFCBase_decref((CFCBase*)final);
CFCParcel_reap_singletons();
}