#!/usr/bin/perl -w # 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. #break1 - Thrift method removed from service base #break2 - Struct field changed in test_struct1(i16 to i32) #break3 - Struct field changed in test_struct1(enum1 to enum2) #break4 - Field type changed in test_struct1(bool to string) #break5- member field type changed in test_struct1(bool to list) #break6- Field type changed in test_struct2 (list to list) #break7 - requiredness removed in struct6 #break8 - requiredness addedd in struct5 #break9 - Struct field removed from struct1 #break10 - Struct field removed from struct2 id = 1 #break11 - Struct field removed from struct3 last id #break12 - derived1_function1 return type changed from enum1 to enum2 #break13 - derived1_function6 return type changed from struct1 to struct2 #break14 - derived1_function4 return type changed from string to double #break15 - derived2_function1 return type changed from list to list #break16 - derived2_function5 return type changed from map to map #break17 - derived2_function6 return type changed from map to map #break18- oneway removed from base_oneway #break19 - oneway added to base_function1 #break20 - first enum value removed from enum1 #break21- last enum value removed from enum2 #break22 - in-between enum value removed from enum1 #break23 - required struct field added to struct4 #break24 - removed inheritance of derived1. #break25 - changed inheritance of derived2. #break26 - Field type changed in base_function1 argument id=3 #break27 - argument changed base_function2 list to list id =8 #break28- derived1_function5 arguement type changed map to list #break29 - base_function2 arguemnt type changed list to string #break30- derived1_function6 argument changed struct1 to map #break31 - Exception removed to base_function2 #break32- Exception1 field type changed for id =1 #break33 - derived1_function1 exception type changed. #break34 - Field added to struct with Field ID being in between two existing field IDs #warning.thrift #Changing defaults #Id=1 struct5 #id=2 struct5 #id=4 struct2(list) #id=3 struct2(list default values removed) #id 4 struct1 change in double value #id 5 struct1 (default string value removed) #id=1 struct3 (change in map values) #id2 struct3 (change in map keys) #change in inheritance for derived1 and derived2 #change in struct field names #id9 struct1 #id2 struct2 use strict; use warnings; use Getopt::Std; # globals my $gArguments = ""; # arguments that will be passed to AuditTool my $gAuditToolPath = ""; my $gPreviousThriftPath; # previous thrift path my $gCurrentThriftPath; # current thrift path my $gThriftFileFolder; my $gBreakingFilesCount =34; my $gVerbose = 0; #functions sub auditBreakingChanges; sub auditNonBreakingChanges; main(); sub main { parseOptions(); auditBreakingChanges(); auditNonBreakingChanges(); } sub parseOptions { my %options = (); if ( getopts ('vf:o:t:',\%options) ) { # current (new) thrift folder if ($options{'f'}) { $gThriftFileFolder = $options{'f'}; $gPreviousThriftPath = $gThriftFileFolder."/test.thrift"; } else { die "Missing Folder containing thrift files\n"; } if($options{'t'}) { $gAuditToolPath = $options{'t'}; } else { die "Audit Tool Path required \n"; } if ($options{'v'}) { $gVerbose = 1; } } } sub auditBreakingChanges { my $breakingFileBaseName = $gThriftFileFolder."/break"; my $newThriftFile; for(my $i=1; $i <= $gBreakingFilesCount; $i++) { $newThriftFile = $breakingFileBaseName."$i.thrift"; my $arguments = $gPreviousThriftPath." ".$newThriftFile; my ($exitCode, $output) = callThriftAuditTool($arguments); print $output if $gVerbose eq 1; if($exitCode == 1) { # thrift_audit returns 1 when it is not able to find files or other non-audit failures print "exiting with exit code =1 i = ".$i."\n"; print $output; exit $exitCode; } if($exitCode != 2) { # thrift-audit return 2 for audit failures. So for Breaking changes we should get 2 as return value. print $output; die "\nTEST FAILURE: Breaking Change not detected for thrift file $newThriftFile, code=$exitCode \n"; } if(index($output,getMessageSubString("break$i")) == -1) { #Audit tool detected failure, but not the expected one. The change in breaking thrift file does not match getMessageSubString() print $output; die "\nTest FAILURE: Audit tool detected failure, but not the expected one!\n"; } else { #Thrift audit tool has detected audit failure and has returned exited to status code 2 print "Test Pass: Audit Failure detected for thrift file break$i.thrift \n"; } } } sub auditNonBreakingChanges { my $breakingFileBaseName = $gThriftFileFolder."/warning"; my $newThriftFile; $newThriftFile = $breakingFileBaseName.".thrift"; my $arguments = $gPreviousThriftPath." ".$newThriftFile; my ($exitCode, $output) = callThriftAuditTool($arguments); print $output if $gVerbose eq 1; if($exitCode == 1) { # thrift_audit returns 1 when it is not able to find files or other non-audit failures print "exiting with exit code = 1 for file warning.thrift\n"; exit $exitCode; } elsif($exitCode != 0) { # thrift-audit return 0 if there are no audit failures. die "\nTEST FAILURE: Non Breaking changes returned failure for thrift file $newThriftFile \n"; } else { #Thrift audit tool has exited with status 0. print "Test Pass: Audit tool exits with success for warnings \n"; } } # ----------------------------------------------------------------------------------------------------- # call thriftAuditTool script sub callThriftAuditTool ( $ ) { my $args = shift; my $command = "$gAuditToolPath --audit $args"; my $output = `$command 2>&1`; my $exitCode = $? >> 8; return ($exitCode,$output); } sub getMessageSubString( $ ) { my $fileName = shift; my %lookupTable = ( "break1" => "base_function3", "break2" => "test_struct1", "break3" => "test_struct1", "break4" => "test_struct1", "break5" => "test_struct1", "break6" => "test_struct2", "break7" => "test_struct6", "break8" => "test_struct5", "break9" => "test_struct1", "break10" => "test_struct2", "break11" => "test_struct3", "break12" => "derived1_function1", "break13" => "derived1_function6", "break14" => "derived1_function4", "break15" => "derived2_function1", "break16" => "derived2_function5", "break17" => "derived2_function6", "break18" => "base_oneway", "break19" => "base_function1", "break20" => "test_enum1", "break21" => "test_enum2", "break22" => "test_enum1", "break23" => "test_struct4", "break24" => "derived1", "break25" => "derived2", "break26" => "base_function1", "break27" => "base_function2_args", "break28" => "derived1_function5_args", "break29" => "base_function2_args", "break30" => "derived1_function6", "break31" => "base_function2_exception", "break32" => "test_exception1", "break33" => "derived1_function1_exception", "break34" => "test_struct3", ); if (not exists $lookupTable{ $fileName }) { print "in the null case\n"; return "NULL"; } my $retval = $lookupTable{ $fileName }; print "$fileName => $retval\n"; return $lookupTable{ $fileName }; }