or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

api-completeness.mdcheckpointing.mddata-generation.mdexecution.mdfault-tolerance.mdindex.mdstreaming.md

api-completeness.mddocs/

0

# API Completeness

1

2

Framework for verifying API parity between Java and Scala implementations using reflection-based method comparison. Essential for ensuring consistent API coverage across language bindings.

3

4

## Core Base Classes

5

6

### ScalaAPICompletenessTestBase

7

8

Abstract base class providing infrastructure for comparing Java and Scala API implementations using reflection.

9

10

```java { .api }

11

public abstract class ScalaAPICompletenessTestBase {

12

protected static final Logger LOG = LoggerFactory.getLogger(ScalaAPICompletenessTestBase.class);

13

14

// Core comparison methods

15

protected void compareApis(Class<?> javaClass, Class<?> scalaClass);

16

protected void compareApis(Class<?> javaClass, Class<?> scalaClass, Set<String> excludedMethods);

17

18

// Method filtering and exclusion

19

protected boolean isExcluded(Method method);

20

protected boolean isExcluded(String methodName);

21

protected Set<String> getExcludedMethods();

22

23

// Comparison utilities

24

protected boolean methodsMatch(Method javaMethod, Method scalaMethod);

25

protected String getMethodSignature(Method method);

26

protected void reportMissingMethods(Set<String> missingMethods, String apiType);

27

}

28

```

29

30

### TupleComparatorTestBase

31

32

Abstract base class for testing tuple comparator implementations between Java and Scala.

33

34

```java { .api }

35

public abstract class TupleComparatorTestBase<T> {

36

protected TypeComparator<T> javaComparator;

37

protected TypeComparator<T> scalaComparator;

38

39

@Before

40

public abstract void setup();

41

42

// Comparison testing methods

43

protected void testComparatorEquality(T value1, T value2);

44

protected void testComparatorSerialization();

45

protected void testComparatorNormalization();

46

47

// Utility methods

48

protected abstract T[] getTestData();

49

protected abstract TypeComparator<T> createJavaComparator();

50

protected abstract TypeComparator<T> createScalaComparator();

51

}

52

```

53

54

### PairComparatorTestBase

55

56

Abstract base class for testing pair comparator implementations.

57

58

```java { .api }

59

public abstract class PairComparatorTestBase<T1, T2> {

60

protected TypePairComparator<T1, T2> javaPairComparator;

61

protected TypePairComparator<T1, T2> scalaPairComparator;

62

63

@Before

64

public abstract void setup();

65

66

// Pair comparison testing

67

protected void testPairComparatorEquality(T1 first1, T2 second1, T1 first2, T2 second2);

68

protected void testPairComparatorSerialization();

69

70

// Abstract methods for test data

71

protected abstract T1[] getFirstTypeTestData();

72

protected abstract T2[] getSecondTypeTestData();

73

protected abstract TypePairComparator<T1, T2> createJavaPairComparator();

74

protected abstract TypePairComparator<T1, T2> createScalaPairComparator();

75

}

76

```

77

78

## API Comparison Utilities

79

80

### MethodSignatureComparator

81

82

Utility for comparing method signatures between Java and Scala implementations.

83

84

```java { .api }

85

public class MethodSignatureComparator {

86

public static boolean signaturesMatch(Method javaMethod, Method scalaMethod);

87

public static String normalizeSignature(Method method);

88

public static boolean returnTypesCompatible(Class<?> javaReturn, Class<?> scalaReturn);

89

public static boolean parametersCompatible(Class<?>[] javaParams, Class<?>[] scalaParams);

90

}

91

```

92

93

### ReflectionTestUtils

94

95

Utilities for reflection-based testing and method analysis.

96

97

```java { .api }

98

public class ReflectionTestUtils {

99

public static Set<Method> getPublicMethods(Class<?> clazz);

100

public static Set<Method> getPublicMethods(Class<?> clazz, Set<String> excludedNames);

101

public static boolean isMethodExcluded(Method method, Set<String> exclusions);

102

public static String getSimpleMethodSignature(Method method);

103

}

104

```

105

106

## Usage Examples

107

108

### Basic API Completeness Test

109

110

```java

111

public class DataSetAPICompletenessTest extends ScalaAPICompletenessTestBase {

112

113

@Test

114

public void testDataSetAPI() {

115

Class<?> javaDataSet = org.apache.flink.api.java.DataSet.class;

116

Class<?> scalaDataSet = org.apache.flink.api.scala.DataSet.class;

117

118

// Compare APIs with standard exclusions

119

compareApis(javaDataSet, scalaDataSet);

120

}

121

122

@Override

123

protected Set<String> getExcludedMethods() {

124

Set<String> excluded = new HashSet<>();

125

126

// Exclude Scala-specific methods

127

excluded.add("$plus$plus"); // Scala ++ operator

128

excluded.add("$colon$colon"); // Scala :: operator

129

130

// Exclude internal methods

131

excluded.add("clean");

132

excluded.add("getExecutionEnvironment");

133

134

return excluded;

135

}

136

137

@Override

138

protected boolean isExcluded(Method method) {

139

// Additional custom exclusion logic

140

String methodName = method.getName();

141

142

// Exclude synthetic methods

143

if (method.isSynthetic()) {

144

return true;

145

}

146

147

// Exclude bridge methods

148

if (method.isBridge()) {

149

return true;

150

}

151

152

// Exclude Scala-specific naming patterns

153

if (methodName.contains("$")) {

154

return true;

155

}

156

157

return super.isExcluded(method);

158

}

159

}

160

```

161

162

### Tuple Comparator Testing

163

164

```java

165

public class Tuple2ComparatorTest extends TupleComparatorTestBase<Tuple2<String, Integer>> {

166

167

@Override

168

public void setup() {

169

javaComparator = createJavaComparator();

170

scalaComparator = createScalaComparator();

171

}

172

173

@Test

174

public void testTuple2Comparison() {

175

Tuple2<String, Integer> tuple1 = new Tuple2<>("hello", 42);

176

Tuple2<String, Integer> tuple2 = new Tuple2<>("world", 24);

177

178

testComparatorEquality(tuple1, tuple2);

179

testComparatorSerialization();

180

testComparatorNormalization();

181

}

182

183

@Override

184

protected Tuple2<String, Integer>[] getTestData() {

185

return new Tuple2[]{

186

new Tuple2<>("a", 1),

187

new Tuple2<>("b", 2),

188

new Tuple2<>("c", 3),

189

new Tuple2<>("a", 1), // Duplicate for equality testing

190

};

191

}

192

193

@Override

194

protected TypeComparator<Tuple2<String, Integer>> createJavaComparator() {

195

return new TupleComparator<>(

196

new int[]{0, 1}, // Key positions

197

new TypeComparator[]{

198

new StringComparator(true),

199

new IntComparator(true)

200

},

201

new TypeSerializer[]{

202

StringSerializer.INSTANCE,

203

IntSerializer.INSTANCE

204

}

205

);

206

}

207

208

@Override

209

protected TypeComparator<Tuple2<String, Integer>> createScalaComparator() {

210

// Create Scala equivalent comparator

211

return new ScalaTupleComparator<>(

212

new int[]{0, 1},

213

new TypeComparator[]{

214

new StringComparator(true),

215

new IntComparator(true)

216

}

217

);

218

}

219

}

220

```

221

222

### Custom API Comparison with Detailed Reporting

223

224

```java

225

public class CustomAPICompletenessTest extends ScalaAPICompletenessTestBase {

226

227

@Test

228

public void testStreamingAPICompleteness() {

229

Class<?> javaDataStream = org.apache.flink.streaming.api.datastream.DataStream.class;

230

Class<?> scalaDataStream = org.apache.flink.streaming.api.scala.DataStream.class;

231

232

Set<String> customExclusions = new HashSet<>();

233

customExclusions.add("javaStream"); // Scala-specific bridge method

234

customExclusions.add("scalaStream"); // Java-specific bridge method

235

236

compareApis(javaDataStream, scalaDataStream, customExclusions);

237

}

238

239

@Override

240

protected void compareApis(Class<?> javaClass, Class<?> scalaClass, Set<String> excludedMethods) {

241

LOG.info("Comparing {} with {}", javaClass.getSimpleName(), scalaClass.getSimpleName());

242

243

Set<Method> javaMethods = getFilteredMethods(javaClass, excludedMethods);

244

Set<Method> scalaMethods = getFilteredMethods(scalaClass, excludedMethods);

245

246

// Find methods in Java but not in Scala

247

Set<String> javaSignatures = javaMethods.stream()

248

.map(this::getMethodSignature)

249

.collect(Collectors.toSet());

250

251

Set<String> scalaSignatures = scalaMethods.stream()

252

.map(this::getMethodSignature)

253

.collect(Collectors.toSet());

254

255

Set<String> missingInScala = new HashSet<>(javaSignatures);

256

missingInScala.removeAll(scalaSignatures);

257

258

Set<String> missingInJava = new HashSet<>(scalaSignatures);

259

missingInJava.removeAll(javaSignatures);

260

261

// Report findings

262

if (!missingInScala.isEmpty()) {

263

reportMissingMethods(missingInScala, "Scala API");

264

}

265

266

if (!missingInJava.isEmpty()) {

267

reportMissingMethods(missingInJava, "Java API");

268

}

269

270

// Assertions

271

assertTrue("Scala API missing methods: " + missingInScala, missingInScala.isEmpty());

272

assertTrue("Java API has extra methods: " + missingInJava, missingInJava.isEmpty());

273

274

LOG.info("API comparison successful: {} methods verified", javaSignatures.size());

275

}

276

277

private Set<Method> getFilteredMethods(Class<?> clazz, Set<String> excludedMethods) {

278

return ReflectionTestUtils.getPublicMethods(clazz, excludedMethods);

279

}

280

}

281

```

282

283

### Comprehensive Operator API Testing

284

285

```java

286

public class OperatorAPICompletenessTest extends ScalaAPICompletenessTestBase {

287

288

@Test

289

public void testAllOperatorAPIs() {

290

// Test core transformation operators

291

testOperatorAPI("DataSet",

292

org.apache.flink.api.java.DataSet.class,

293

org.apache.flink.api.scala.DataSet.class);

294

295

testOperatorAPI("DataStream",

296

org.apache.flink.streaming.api.datastream.DataStream.class,

297

org.apache.flink.streaming.api.scala.DataStream.class);

298

299

testOperatorAPI("KeyedStream",

300

org.apache.flink.streaming.api.datastream.KeyedStream.class,

301

org.apache.flink.streaming.api.scala.KeyedStream.class);

302

}

303

304

private void testOperatorAPI(String apiName, Class<?> javaClass, Class<?> scalaClass) {

305

LOG.info("Testing {} API completeness", apiName);

306

307

try {

308

compareApis(javaClass, scalaClass);

309

LOG.info("{} API comparison successful", apiName);

310

} catch (AssertionError e) {

311

LOG.error("{} API comparison failed: {}", apiName, e.getMessage());

312

throw new AssertionError(apiName + " API completeness test failed", e);

313

}

314

}

315

316

@Override

317

protected Set<String> getExcludedMethods() {

318

Set<String> excluded = super.getExcludedMethods();

319

320

// Common exclusions across all operator APIs

321

excluded.add("returns"); // Type hint methods

322

excluded.add("getType");

323

excluded.add("getTransformation");

324

325

// Scala collection interop methods

326

excluded.add("asScala");

327

excluded.add("asJava");

328

329

return excluded;

330

}

331

}

332

```

333

334

### Serialization Compatibility Testing

335

336

```java

337

public class SerializationAPICompletenessTest extends ScalaAPICompletenessTestBase {

338

339

@Test

340

public void testSerializerAPIs() {

341

compareSerializerAPIs(

342

"Tuple2Serializer",

343

org.apache.flink.api.common.typeutils.base.TupleSerializer.class,

344

org.apache.flink.api.scala.typeutils.ScalaTupleSerializer.class

345

);

346

}

347

348

private void compareSerializerAPIs(String serializerName, Class<?> javaClass, Class<?> scalaClass) {

349

LOG.info("Comparing {} implementations", serializerName);

350

351

// Get serialization-specific methods

352

Set<Method> javaMethods = Arrays.stream(javaClass.getMethods())

353

.filter(m -> isSerializationMethod(m))

354

.collect(Collectors.toSet());

355

356

Set<Method> scalaMethods = Arrays.stream(scalaClass.getMethods())

357

.filter(m -> isSerializationMethod(m))

358

.collect(Collectors.toSet());

359

360

// Compare method signatures

361

compareMethodSets(javaMethods, scalaMethods, serializerName);

362

}

363

364

private boolean isSerializationMethod(Method method) {

365

String name = method.getName();

366

return name.equals("serialize") ||

367

name.equals("deserialize") ||

368

name.equals("getLength") ||

369

name.equals("copy");

370

}

371

372

private void compareMethodSets(Set<Method> javaMethods, Set<Method> scalaMethods, String context) {

373

Set<String> javaSignatures = javaMethods.stream()

374

.map(this::getMethodSignature)

375

.collect(Collectors.toSet());

376

377

Set<String> scalaSignatures = scalaMethods.stream()

378

.map(this::getMethodSignature)

379

.collect(Collectors.toSet());

380

381

assertEquals(context + " method signatures should match", javaSignatures, scalaSignatures);

382

}

383

}